Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loader can not be restarted after orientation changed

Tags:

android

I'd like to use a demo to show this:

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

    Button button = (Button) findViewById(R.id.button1);
    button.setOnClickListener(buttonClickListener);
}

private OnClickListener buttonClickListener = new OnClickListener() {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        startMyLoader();
    }

};

private void startMyLoader() {
    getLoaderManager().destroyLoader(0);
    getLoaderManager().restartLoader(0, null, myLoaderListener);
}

/**
 * The listener for the group metadata loader.
 */
private final LoaderManager.LoaderCallbacks<Cursor> myLoaderListener 
    = new LoaderCallbacks<Cursor>() {

    @Override
    public CursorLoader onCreateLoader(int id, Bundle args) {
        return new CursorLoader(LoaderDemoActivity.this, 
        ContactsContract.Contacts.CONTENT_URI, 
        null, null, null, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        cursor.moveToPosition(-1);
        if (cursor.moveToNext()) {
            Context context = getApplicationContext();
            CharSequence text = "Load finished!";
            int duration = Toast.LENGTH_SHORT;

            Toast toast = Toast.makeText(context, text, duration);
            toast.show();
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
    }
};
enter code here

After orientation changed, I clicked the button, the onCreateLoader can be called, but onLoadFinished will not be called.

It seems strange.

Thanks for help in advance.

like image 213
user1335719 Avatar asked Apr 16 '12 07:04

user1335719


4 Answers

I faced the same problem. Please make a try call this.getSupportLoaderManager() in onCreate. It solved my problem. Hope it will help you as well

like image 61
Senkethya Sar Avatar answered Nov 05 '22 23:11

Senkethya Sar


I think I have found the reason.

In Activity onCreate, it will load all the LoaderMangers(of its own or its sub-Fragments) from NonConfigurationInstances.

    if (mLastNonConfigurationInstances != null) {
        mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
    }

And in Activity onStart, it will try to start its own LoaderManger.

    if (!mLoadersStarted) {
        mLoadersStarted = true;
        if (mLoaderManager != null) {
            mLoaderManager.doStart();
        } else if (!mCheckedForLoaderManager) {
            mLoaderManager = getLoaderManager(-1, mLoadersStarted, false);
        }
        mCheckedForLoaderManager = true;
    }

But after config changed, mLoaderManager == null, so it will not start it. And here is the problem! If you try to start loader belong to this loaderManager, it will fail.

void installLoader(LoaderInfo info) {
    mLoaders.put(info.mId, info);
    if (mStarted) {
        // The activity will start all existing loaders in it's onStart(),
        // so only start them here if we're past that point of the activitiy's
        // life cycle
        info.start();
    }
}

note the mStarted value which will be set 'true' when LoaderManager started.

And there is two ways to solve this problem.

  1. call getLoaderManger() in onCreate(), it will re-assign the mLoaderManager and make it ready to be started in the subseuqent onStart().

    public LoaderManager getLoaderManager() { if (mLoaderManager != null) { return mLoaderManager; } mCheckedForLoaderManager = true; mLoaderManager = getLoaderManager(-1, mLoadersStarted, true); return mLoaderManager; }

  2. have the loader located in fragments. Because in Fragments' onStart(), it will start its own LoaderManager.

    if (!mLoadersStarted) { mLoadersStarted = true; if (!mCheckedForLoaderManager) { mCheckedForLoaderManager = true; mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false); } if (mLoaderManager != null) { mLoaderManager.doStart(); } }

like image 36
user1335719 Avatar answered Nov 05 '22 23:11

user1335719


You don't need to (neither ought to) destroy your Loader in order to reload it. Loader class is intended to be reusable.

Use initLoader instead. eg.:

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

If you want to force reloading allready registered loader:

getLoaderManager().getLoader(0).forceLoad();

If you are not sure if the Loader instance allready exists after configuration change event happened use initLoader instead of getLoader to retrieve your Loader instance on which you can call forceLoad().

getLoaderManager().initLoader(0, null, myLoaderListener).forceLoad();

If you use support library then use forceLoad even after first instantation - there is probably a bug - I remind myself there are some questions about it on this forum - try searching older posts.

like image 3
Tomasz Gawel Avatar answered Nov 05 '22 22:11

Tomasz Gawel


Make sure you are not checking savedStateInfo while using fragments before you call your loader in activity onCreate

@Override
public void onCreate(Bundle savedInstanceState) {

    // used to not overlap fragments
    if (savedInstanceState != null) {
        return null;
    }

    loadFragments();

    getSupportLoaderManager().restartLoader(LISTS_LOADER, null, this);
}

If you need to check for savedInstanceState fragments anyway you can check for any class variable that should be created after loader finished loading, as activity gets destroyed when rotating, but raising from previous state when rotating back.

like image 1
Artem L Avatar answered Nov 05 '22 23:11

Artem L