Will fragments in a ViewPager sometimes get garbage collected? An AsyncTask / ListView / ViewPager issue

343 Views Asked by At

I have an app that downloads data from the internet using an AsyncTask. That information is then split and displayed in two ListViews, one in each of two fragments. Those fragments are inside a ViewPager so I can swipe between them.

The problem is that now that I have two fragments, knowing when it is okay to have the AsyncTask call setAdapter() on either fragments' lists is tricky since they might not be created yet.

I used to only have one Fragment/ListView/ListAdapter, which made this very easy because I could put the call to the AsyncTask in the Adapter's constructor and know for sure the ListView is instantiated. I can't do that any more since now I have to wait for two ListViews to be created.

-=-=-=-=-

My solution was to use semaphores to signal when both were created, and use them to only allow the AsyncTask to try and set the adapters after the fragments were created.

That seemed to work, but unfortunately I'm still getting occasional force closes due to the ListViews still sometimes being null despite the semaphore suggesting they should have been created.

-=-=-=-=-

That leads to my question: Is it possible for fragments in a ViewPager to not always be created, or to be created twice, or to be deleted after having been created? I'm having trouble figuring out what else might be causing this.

Alternatively, if someone has a suggestion as to a better way to approach this problem than using semaphores I'm all ears.

2

There are 2 best solutions below

1
On

Fragments inside a ViewPager can be destroyed and recreated frequently when sliding back and forth. I am under the impression (though have not verified it) that this is more likely to happen when the fragment uses more memory (e.g, because it contains a bitmap).

You can verify this by adding Log-outputs to the onCreateView and onDestroyView methods of your fragments.

Proposing a better solution than the one you have is difficult without the code, but it sounds like decoupling the data collection in the AsyncTask from the data display in the ListViews would help. For example: implement a local data store in the Activity that holds the ViewPager and have the ListView-Adapters tap into that data store.

0
On

The only way to be reasonably sure you're background jobs are going to finish is by running them in a foreground service (but even than android can decide to kill it, but it will be restarted when resources become available again).

What I do is store the received data in the Application class and the fragments register them-self with their activity using an interface. Something like this, works like a charm:

public class Fragment implements MyFragmentInterface {
    MyActivity mActivity;

    public Interface MyFragmentInterface {
        public void onDataAvailable(List<> data);
    }

    @Override
    public void onDataAvailable(List<> data) {
        //Do with the data what needs to be done
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        if ( activity instanceof MyActivity ) {
            mActivity=activity;
        } else {
            throw new IllegalArgumentException("Activity must be ...");
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        if ( mActivity != null ) {
            activity.setListener(this);
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        if ( mActivity != null ) {
            mActivity.setListener(null);
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();

        mActivity=null;
    }
}