LoaderCallbacks.onLoadFinished is not called at bootloader loading and contains data

I have 1 activity and 2 fragments. Both fragments use the custom AsyncTaskLoader to retrieve some data from the web service, and since I use Loader, it needs to store activity data and re-create fragments. Both fragments override the onActivityCreated method and call getLoaderManager (). InitLoader (0, null, this), which either creates a new one or reuses the existing bootloader.

When an activity is first created, it adds Fragment # 1 to FrameLayout by default, loads the data, internally calls the LoaderCallbacks.onLoadFinished () method and displays the result. I have a button that replaces fragment # 1 with fragment # 2 per click, and fragment # 1 with a fragment-stack fragment. When the user presses the BACK key, he switches to Snippet # 1.

onActivityCreated is called again on fragment # 1, and then again calls iniLoader (). This time, the data already exists in the loader, and I expect it to call the LoaderCallbacks.onLoadFinished method again, since it already has data available, as described here: http://goo.gl/EGFJk

Provides initialization and activation of the bootloader. If the bootloader does not exist yet, it is created and (if the activity / fragment is currently running) starts the bootloader. Otherwise, the last bootloader created will be reused. In any case, this callback is associated with the loader and will be called as a change in the state of the loader. If the calling subscriber is in a running state and the requested bootloader already exists and generated its data, then callback onLoadFinished (Loader, D) will be called immediately (inside this function), so you should be prepared for this to happen.

But this method is never called, even if the loader exists and generated data ready to be sent.


Change # 1 Problem from the point of view of users:

  • The user starts the activity and sees fragment1 with some data
  • The user clicks on what changes the first fragment to another, with different data
  • User presses the BACK key
  • The user now looks at fragment1 again, but there is no data. (which means I need to get it from the webservice again - and I would like to avoid this if possible).

Here 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 snippet # 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 } } 

Does anyone know a solution to this or what I'm doing wrong here? Any help is appreciated.

+4
source share
4 answers

The correct answer, at least for me, was to move all Loader initialization from onViewCreated or onActivityCreated to onStart .

After that, it works great!

+1
source

From my point of view, onLoadFinished will be called only for the first time, when the download is already completed, it ends only once for both fragments. What you can do is to save the result in the property in activity and check it in the second fragment creation.

0
source

Update:. After further investigation, I found that my original answer was wrong. restartLoader also destroy the bootloader if it already exists.

Nevertheless, I solved my problem. I create a standard CursorLoader in onCreateLoader and change the cursor to my CursorAdapter in onLoadFinished . I needed to call initLoader after initializing the CursorAdapter . Now the data still exists when the fragment returns from the backstack.

Original answer: I found two possible ways to solve this problem.

Apparently, when initLoader(int id, Bundle args, LoaderCallbacks<D> callback) is called a second time on onActivityCreated(Bundle savedInstanceState) after the Fragment returns from backstack, the onLoadFinished(Loader<String> loader, String result) method never called (as you described).

However, calling restartLoader(int id, Bundle args, LoaderCallbacks<D> callback) after or instead of initLoader will result in an onLoadFinished call. To improve performance, I use a boolean argument to determine if Fragment is a new instance or not. Then I call restartLoader only if the Snippet returns from Backstack.

As far as I can tell, the old data is stored in the bootloader and does not restart, but I'm not sure. The second possibility (especially if you do not use the stack, but instead create a new instance of the fragment in the transaction) should call destroyLoader(int id) before the fragment disappears (for example, in onPause ).

0
source

I already had this problem, I can not explain why this error, but I know this line:

 getLoaderManager().initLoader(0, null, this); 

do not work in my code, so you can change it for this:

 LoaderManager lm = getLoaderManager(); lm.initLoader(LOADER_ID, null, this); 

The code runs the methods:

 onCreateLoader 

you can try...

-1
source

All Articles