3

I am observing significant behavioral differences between the native LoaderManager and the version found in the support library. Specifically with regards to the retain behavior during configuration changes.

I originally began testing this behavior because I noticed the following bug with the support library LoaderManager, and I wanted to see if it worked better with the native APIs: Loader unable to retain itself during certain configuration change

I have written a simple test Activity to display the issue:

public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks<String>, View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = new Button(this);
        button.setText("Click Me");
        button.setOnClickListener(this);

        setContentView(button);
    }

    @Override
    protected void onResume() {
        super.onResume();
        getLoaderManager().initLoader(1, null, this);
    }

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

    @Override
    public void onLoadFinished(Loader<String> loader, String data) {
        Log.d("LoaderTest", "Loader Finished "+loader.toString());
    }

    @Override
    public void onLoaderReset(Loader<String> loader) {
        Log.d("LoaderTest", "Loader Reset "+loader.toString());
    }

    @Override
    public void onClick(View v) {
        startActivity(new Intent(this, SecondActivity.class));
    }

    private static class StringLoader extends Loader<String> {

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

        @Override
        protected void onStartLoading() {
            deliverResult("Hi Mom!");
        }
    }
}

The SecondActivity in this example is just a dummy and can be anything. It was originally added to test the Launch -> Rotate -> Return case described in the linked SO issue (which still doesn't work in the support library today).

Running this application with the android.support.v4.FragmentActivity and Loader components from android.support.v4 as well, the behavior works as expected (minus the bug described in the link above). One can rotate the device multiple times, and on each rotation:

  • The same LoaderManager instance is returned from getLoaderManager()
  • The same Loader instance is passed to onLoadFinished()
  • onLoadFinished() is called immediately after each call to initLoader()

Now, use the example above with all "native" components for Activity, LoaderManager, AsyncTaskLoader, etc. and the behavior changes dramatically. Rotate the device again a few times and:

  • Now onLoadFinished() will only be called every other rotation
  • When onLoadFinished() is called, the Loader instance passed in is always new
  • LoaderManager is also a different instance during these times
  • On the even rotations, the LoaderManager instance doesn't change and it does return the same Loader from initLoader(); however, it's onLoadFinished() never fires because the LoaderManager claims to not have been started (i.e. it's internal mStarted is false)

I truly hope I'm missing something quite obvious, because otherwise the behavior difference between the native LoaderManager versus the support library version proves to be a big problem. Especially as developers move off the support library and back into the native APIs.

Can anyone verify this behavior or expose what I'm doing wrong?

Community
  • 1
  • 1
devunwired
  • 62,780
  • 12
  • 127
  • 139
  • Why would you move off the support versions? They'll always be the more consistent versions across all platforms versions and I'm sure most apps will have a reason to use something from it (be it `ViewPager` or something else). – ianhanniballake Dec 17 '13 at 06:57
  • If `ViewPager` is your only concern, there is a v13 version of the support library that doesn't include the bulk of all the other APIs. It's a bit short-sighted to think that at some point in the future we would want to stop including back-ported code inside the APK once the minimum version supports that decision. Unless, of course, there native implementation is broken :) – devunwired Dec 17 '13 at 15:00
  • I only use `ViewPager` as an example. Nested Fragments, `PrintHelper`, `getExternalCacheDirs`, `ActionBarDrawerToggle`/`NavigationDrawer`, the entire mediarouter framework, and others are all reasons you'd continue to use the support library and with ProGuard the APK impact is much less of a concern. Also note that the v13 version of the support library includes the entire v4 support library so you aren't saving anything there. – ianhanniballake Dec 17 '13 at 17:37
  • Does the same behavior occur if you move `initLoader()` out of `onResume()` and into `onCreate()`? – Alex Lockwood Dec 18 '13 at 08:22
  • Also, have you tried setting `LoaderManager.enableDebugLogging(true)`? You never know... it might help. – Alex Lockwood Dec 18 '13 at 08:32
  • What API level did you find the broken behavior on? – louielouie Dec 18 '13 at 17:06
  • @AlexLockwood you can try the example for yourself (which is what I was hoping people would do), the behavior doesn't seem to change regardless of where the Loader is initialized. – devunwired Dec 18 '13 at 18:22
  • @louielouie This is consistent on everything from ICS to KitKat so far. – devunwired Dec 18 '13 at 18:22

0 Answers0