Home » Android » android – How to handle network calls inside a Fragment

android – How to handle network calls inside a Fragment

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have the following situation:

I have an Activity that hosts a ViewPager, and I have 4 Fragments;

the ViewPager at the beginning contains Fragment A,

when the user swipes on the ViewPager Fragment B goes into the ViewPager, then Fragment C and Fragment D …etc…

Now as soon as the FragmentPagerAdapter is instantiated at least 2 of the Fragments are created.

This poses 2 problem:

  1. Every Fragment needs to perform network calls, but I do not want to do unnecessary ones (I do not want to make network calls for Fragment B, if the user never swipes to Fragment B );
  2. similar to 1.), I need to show a ProgessDialog when a Fragment perform network calls, but I do not want to show dialogs from Fragment B if the user never goes to it…

Please what kind of pattern should I use in such a circumstance?

Activity

public class PagerActivity extends ActionBarActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager_layout);

ViewPager pager=(ViewPager)findViewById(R.id.pager);
TabPageIndicator tabs=(TabPageIndicator)findViewById(R.id.titles);

pager.setAdapter(buildAdapter());
tabs.setViewPager(pager);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}

FragmentPagerAdapter

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {


@Override
public int getCount() {
    return (4);
}

@Override
public Fragment getItem(int position) {


    if (position == 1) {
        if (dashbardFragment == null)
            dashbardFragment = DashBoardFragment.newInstance(position);
        return dashbardFragment;
    }
    if (position == 0) {
        if (listOfParticipantFragment == null)
            listOfParticipantFragment = ListOfParicipantsFragment
            .newInstance(position);
        return listOfParticipantFragment;
    }

}

1 Fragment

public class ListOfParicipantsFragment extends Fragment {

public static ListOfParicipantsFragment newInstance(int position) {
    ListOfParicipantsFragment frag = new ListOfParicipantsFragment();
    return (frag);
}

public static String getTitle(Context ctxt, int position) {
    return myApplication.getContext().getResources().getString(R.string.list_of_participants_fragment_title);
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View result = inflater.inflate(R.layout.guest_list_fragment_layout,
            container, false);

    return (result);
}
How to&Answers:

Try this, in each fragment override below method and call your function when it is visible:

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if(isVisible()){
        if(isVisibleToUser){
            Log.d("MyTag","My Fragment is visible");
        }else{
            Log.d("MyTag","My Fragment is not visible");
        }
    }
}

EDIT

Note: This is only useful when using a FragmentPagerAdapter or FragmentStatePagerAdapter

Answer:

Basically what you want to do, is, find which fragment is currently being viewed when you swipe. And then, do your network calls.

You can take advantage of the ViewPager listeners to get notified when the user swipes to a new page. Docs : http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html#onPageSelected(int)

This will give you the position of the View. But i’m assuming that what you want is the actual fragment, which is a bit more tricky.

But, this has been answered already in here : Is it possible to access the current Fragment being viewed by a ViewPager?

Hope it helps

Answer:

Let me introduce my idea to you:

  • getCurrentItem()
    Method of ViewPager
  • getItem(int position)
    Method of FragmentPagerAdapter Return the Fragment associated with a specified position.

You can define an Interface holding the method for Network I/O like

public Interface INetworkOnFragment{
 void handle(){
         //...
           }  
}

And implement it on your fragments and handle their own business logic (Network calls).

In main Activity ,set ViewPager.OnPageChangeListener on ViewPager object like here:

    pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener(){
public void onPageScrollStateChanged(int state){
//donothing
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
//donothing
}
public void onPageSelected(int position){
       INetworkOnFragment interface =(INetworkOnFragment) (pager.getAdapter().getItem(position));//get the current fragment and call handle method on it,dont need to care about whichever fragment it is . 
interface.handle()
}
});

The most important is onPageSelected(int position),Inside the callback it get the current fragment and call handle method on it,dont need to care about whichever fragment it is .

Remember the handle method are called in Activity and not in fragments.All the Network calls are implemention of Interface,which make it easy to deal in Activity.

Answer:

Check out the solution from my answer here

1) Create LifecycleManager Interface The interface will have two methods (onPauseFragment and onResumeFragment) and each ViewPager’s Fragment will implement it
2) Let each Fragment implement the interface
3) Implement interface methods in each fragment – start your AsyncTask in onResumeFragment
4) Call interface methods on ViewPager page change You can set OnPageChangeListener on ViewPager and get callback each time when ViewPager shows another page
5) Implement OnPageChangeListener to call your custom Lifecycle methods

Answer:

Create a page into view method for FragmentStatePagerAdapter which calls a method on the fragment when the fragment comes into view.

Implement the OnPageIntoView interface in your fragment.

public class SomethingDifferent extends Fragment implements OnPageIntoView {
...
/*
 * Called when this page comes into view
 * 
 * @see com.gosylvester.bestrides.SettingFragmentPagerSupport.MyAdapter.
 * OnPageIntoView#onPageIntoView()
 */
@Override
public void onPageIntoView() {
    // this is just some random example code
    // that does some heavy lifting it only runs when the fragment
    // frist comes into view
    if (fragmentActivity != null) {
        if (lrc == null) {
            lrc = new ClientServiceLocationRecorder(
                    new WeakReference<Context>(
                            fragmentActivity.getApplicationContext()),
                    lrcCallback);
        }

        // get a status message from the location recorder
        lrc.sndMessageToLocationRecorder(ServiceLocationRecorder.MSG_RECORD_STATUS);
    }
}

Create a custom FragmentStatePagerAdapter Override the setPrimaryItem method and if the object can be cast to the interface then call through the interface one time only.

public static class MyAdapter extends FragmentStatePagerAdapter {

    public interface OnPageIntoView {
        public void onPageIntoView();
    }

    private Fragment mCurrentFragment;

    //bonus method to get the current fragment
    public Fragment getCurrentFragment() {
        return mCurrentFragment;
    }

    static int lastPosition = -1;

    @Override
    public void setPrimaryItem(ViewGroup container, int position,
            Object object) {
        //quickly determine if the primary item has changed
        //and one time only call through interface
        if (position != lastPosition) {
            lastPosition = position;
            //determine if this is fragment it should be but lets avoid 
            //class cast exceptions
            if (Fragment.class.isAssignableFrom(object.getClass())) {
                mCurrentFragment = ((Fragment) object);
                //determine if the onPageIntoView interface has
                //been implemented in the fragment
                //if so call the onPageIntoView
                if (OnPageIntoView.class.isAssignableFrom(mCurrentFragment
                        .getClass())) {
                    ((OnPageIntoView) mCurrentFragment).onPageIntoView();
                }
            }
        }
        super.setPrimaryItem(container, position, object);
    }
}

Answer:

It seems easy to me,what you need is Fragments onResume() method. This will be called only when your fragment is VISIBLE to user.

Here you can add logic to initiate your network call. It guarantees that your fragment is in visible mode.

See this

enter image description here

However you can optimize your network calls logic, using LoaderManager with AsyncTaskLoader pattern.

Loaders take care of screen orientation changes & they cache data for you. So that network call is not initiated twice for same operation.

From Android documentation

Introduced in Android 3.0, loaders make it easy to asynchronously load
data in an activity or fragment. Loaders have these characteristics:

They are available to every Activity and Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results 

when the content changes.
They automatically reconnect to the last loader’s cursor when
being recreated after a configuration change.
Thus, they don’t need to re-query their data.

You can use any Asynchronous HTTP lib for network calls like Retrofit

i found one tutorial for AsyncTaskLoader & LoaderManager @ this link, below are some quotes from tutorial

Loaders aren’t trivial, so why use them in the first place? Well, in
most cases, you would use them in the same scenarios where you’ve been
using AsyncTasks; in fact, some loader subclasses extend AsyncTask.
Just as AsyncTasks are used to perform any long-running operation that
would tie up the user thread and ultimately throw the dreaded
Application Not Responding (ANR), loaders perform in the same manner
with the same purpose. The main difference is loaders are specialized
for loading data. As such, loaders offer a number of efficiency and
convenience benefits.

Answer:

@Andrew Carl provide good idea. I also use the similar approach in my projects. I think it’s more generalized.

Create an interface:

public interface ViewPagerFragment {
    void onSelected();

    void onDeselected();
}

And this common helper:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;

public class ViewPagerHelper implements ViewPager.OnPageChangeListener {
    private final FragmentManager mFragmentManager;
    private final ViewPager mViewPager;
    private int mSelectedPage;

    public ViewPagerHelper(FragmentManager fragmentManager, ViewPager viewPager) {
        mFragmentManager = fragmentManager;
        mViewPager = viewPager;
        mSelectedPage = -1;
    }

    @Override
    public void onPageSelected(int position) {
        Fragment previous = findViewPagerChildFragment(mFragmentManager, mViewPager, mSelectedPage);
        if (previous instanceof ViewPagerFragment) {
            ((ViewPagerFragment) previous).onDeselected();
        }

        Fragment current = findViewPagerChildFragment(mFragmentManager, mViewPager, position);
        if (current instanceof ViewPagerFragment) {
            ((ViewPagerFragment) current).onSelected();
        }

        mSelectedPage = position;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // empty
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        // empty
    }

    public static Fragment findViewPagerChildFragment(FragmentManager manager, ViewPager pager, int position) {
        if (pager == null) {
            return null;
        }

        String tag = "android:switcher:" + pager.getId() + ":" + position;
        return manager.findFragmentByTag(tag);
    }
}

Now you may use them for any purpose:

Fragment:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

public class MyFragment extends Fragment implements ViewPagerFragment {
    private boolean mSelected;

    public static MyFragment newInstance(int position) {
        Bundle args = new Bundle();
        args.putInt("position", position);

        MyFragment result = new MyFragment();
        result.setArguments(args);
        return result;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        TextView result = new TextView(inflater.getContext());
        result.setText("Position: " + getPosition());
        return result;
    }

    private int getPosition() {
        return getArguments().getInt("position");
    }

    @Override
    public void onSelected() {
        mSelected = true;
        start();
    }

    @Override
    public void onDeselected() {
        mSelected = false;
    }

    private void start() {
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Activity activity = getActivity();
                if (activity == null) {
                    return;
                }

                if (!mSelected) {
                    Toast.makeText(activity, "Fragment #" + getPosition() + " stopped", Toast.LENGTH_SHORT).show();
                    return;
                }

                TextView textView = (TextView) activity.findViewById(R.id.text);
                if (textView != null) {
                    textView.setText("Fragment #" + getPosition() + " works: " + System.nanoTime() % 10000);
                }

                handler.postDelayed(this, 150);
            }
        }, 150);
    }
}

Activity:

import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
        viewPager.setOnPageChangeListener(new ViewPagerHelper(getSupportFragmentManager(), viewPager));
        viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
    }
}

Adapter:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class MyAdapter extends FragmentPagerAdapter {
    public MyAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(position);
    }

    @Override
    public int getCount() {
        return 42;
    }
}

Check complete demo on github.