Java – LoaderCallbacks.onLoadFinished is not called when the loader is reused and contains data

LoaderCallbacks.onLoadFinished is not called when the loader is reused and contains data… here is a solution to the problem.

LoaderCallbacks.onLoadFinished is not called when the loader is reused and contains data

I have 1 activity and 2 fragments. Both fragments use a custom AsyncTaskLoader to get some data from the web service, and since I’m using a loader, it should recreate the persisted data across activities and fragments. Both fragments override the onActivityCreated method and call getLoaderManager().initLoader(0, null, this) to create a new loader or reuse an existing loader.

When an activity is first created, Fragment #1 is added to the FrameLayout by default, the data is loaded, and the LoaderCallbacks.onLoadFinished() method is called internally and the result is displayed. I have a button that replaces Fragment #1 with Fragment #2 when clicked, and Fragment #1 is pushed to the fragment-backstack. When the user presses the BACK key, it switches back to Fragment #1.

onActivityCreated is called again on Fragment #1, and then apparently iniLoader() again. This time the data is already present in the loader, and I want it to automatically call the LoaderCallbacks.onLoadFinished method again because it already has data available, as follows: http://goo.gl/EGFJk

Ensures a loader is initialized and active. If the loader doesn’t already exist, one is created and (if the activity/fragment is currently started) starts the loader. Otherwise the last created loader is re-used.
In either case, the given callback is associated with the loader, and will be called as the loader state changes. If at the point of call the caller is in its started state, and the requested loader already exists and has generated its data, then callback onLoadFinished(Loader, D) will be called immediately (inside of this function), so you must be prepared for this to happen.

But even if the loader exists and has generated data ready for delivery, the method is never called.


Edit #1
Questions from the user’s perspective:

  • The user starts the activity and sees fragment1 with some data
  • The user clicks somewhere to change the first fragment to another fragment, with a different one
    Data
  • The user presses the back key
  • The user now looks at fragment1 again, but there is no data. (This means I need to get it from the web service again – I want to avoid this if possible)

This is my Activity :

public class FragmentTestsActivity extends Activity implements OnClickListener {

private Button btn1;

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

btn1 = (Button) findViewById(R.id.btn1);
        btn1.setOnClickListener(this);

Fragment newFragment = new Fragment1();
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.add(R.id.fragmentContainer, newFragment).commit();
    }

@Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.btn1) {
            showNewFragment();
        }
    }

public void showNewFragment() {
         Instantiate a new fragment.
        Fragment2 newFragment = new Fragment2();

 Add the fragment to the activity, pushing this transaction
         on to the back stack.
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.fragmentContainer, newFragment);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        ft.addToBackStack(null);
        ft.commit();
    }
}

My fragment #1:

public class Fragment1 extends Fragment implements LoaderCallbacks<String> {

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment1, container, false);
    }

@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

LoaderManager.enableDebugLogging(true);        
        getLoaderManager().initLoader(0, null, this);
    }

private static class TestLoader extends AsyncTaskLoader<String> {

String result;

public TestLoader(Context context) {
            super(context);
        }

@Override
        public String loadInBackground() {
             Some long-running call to a webservice - replaced with a simple string to test with            
            return "FirstTimeData";
        }

@Override
        public void deliverResult(String data) {
            result = data;

if (isStarted()) {
                super.deliverResult(data);
            }
        }

@Override
        protected void onStartLoading() {
            if (result != null) {
                deliverResult(result);
            }
            if (takeContentChanged() || result == null) {
                forceLoad();
            }
        }

@Override
        protected void onStopLoading() {
            cancelLoad();
        }     
    }

@Override
    public Loader<String> onCreateLoader(int id, Bundle args) {
        return new TestLoader(getActivity());
    }

@Override
    public void onLoadFinished(Loader<String> loader, String result) {
        Log.d("Fragment1", "onLoadFinished: " + result);
    }

@Override
    public void onLoaderReset(Loader<String> loader) {
         TODO Auto-generated method stub
    }
}

Anyone know the solution to this problem or am I doing something wrong here? Any help is greatly appreciated.

Solution

The correct answer, at least for me, is to move the entire Loader initialization from onViewCreated or onActivityCreated to onStart.

Everything worked fine after that!

Related Problems and Solutions