Почему CursorLoader onLoaderReset() вызывается после вращения устройства?

У меня есть основное действие A, которое использует CursorLoader для запроса БД. Это я создаю в методе onCreate():

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    getSupportLoaderManager().initLoader(LOADER_MEASUREMENTS, null, A.this);
}

Действие A также реализует 3 обратных вызова для CursorLoader:

public Loader<Cursor> onCreateLoader(int loaderId, Bundle args)
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
public void onLoaderReset(Loader<Cursor> loader)

Когда я поворачиваю устройство, я вижу, как работают правильные методы жизненного цикла:

A.onPause()
A.onStop()
A.onDestroy()          
A.onCreate()      <-- re-connect to existing loader, onCreateLoader() not called
A.onLoadFinished()
A.onStart()
A.onResume()

Затем я открываю суб-активность B и поворачиваю свое устройство. Когда я заканчиваю B и возвращаюсь к Activity A, я вижу следующий прогон:

B.onPause()
       A.onLoaderReset()      <- why does this run?
       A.onDestroy()          
       A.onCreate()
       A.onCreateLoader()     <- now runs as loader is null
       A.onStart()
       ...

Почему мой загрузчик reset, потому что я активировал Activity B и вращал устройство? Просто добавьте, что действие B не имеет ничего общего с БД или CursorLoader.

Ответ 1

Я проверил LoaderManager исходный код, вы найдете этот метод:

/**
     * Stops and removes the loader with the given ID.  If this loader
     * had previously reported data to the client through
     * {@link LoaderCallbacks#onLoadFinished(Loader, Object)}, a call
     * will be made to {@link LoaderCallbacks#onLoaderReset(Loader)}.
     */
    public abstract void destroyLoader(int id);

Похоже, что загрузчик уничтожается при вращении экрана (из-за изменения конфигурации). LoaderManager внутренне вызывает метод destroyLoader, который в свою очередь вызывает метод обратного вызова onLoaderReset.

Ответ 2

Кажется, что это имеет какое-то отношение к LoaderManager и не сохраняет состояние активности.

LoaderManager управляется android.app.FragmentHostCallback и void doLoaderStop(boolean retain) в этом классе, похоже, делает разницу. В зависимости от аргумента он будет либо retain(), либо stop() его загрузчики.

При изменении конфигурации в действии A (поворот экрана) действие уничтожается и сразу воссоздается. В ActivityThread#handleRelaunchActivity() значение mChangingConfigurations для активности устанавливается равным true. Это важно, потому что когда вы останавливаете свою деятельность при изменении конфигурации, она вызывается в Activity:

final void performStop() {
    mDoReportFullyDrawn = false;
    mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
    // ...
}

Вы можете попытаться глубже погрузиться в то, что происходит, и ваша установка для Android должна иметь источники, а grep - это потрясающе сделать это & ​​mdash, но я просто подведу итог остальным.

Отказ от ответственности: я не полностью проверил следующее, но это то, что я понимаю, происходит.

Как видно выше, когда активность видна подходящей для перезапуска, она будет удерживать загрузчики вместо их остановки. Когда вы вращаете активность A, загрузчики сохраняются. Когда вы вращаете активность B, активность B сразу восстанавливается, а активность A просто уничтожается. Вопреки ранее, погрузчики будут остановлены.

Остановленные загрузчики можно просто уничтожить и воссоздать, как видно, с помощью LoaderManager, и это то, что происходит с вашим.

Если вы хотите выглядеть хорошо, проверьте:

  • app/LoaderManager или v4/app/LoaderManager
  • app/FragmentHostCallback
  • Activity и ActivityThread

Ответ 3

Вы можете просто установить контрольную точку внутри A.onLoaderReset() и запустить приложение в режиме отладки, чтобы увидеть трассировку стека для устранения неполадок, используя что-то вроде:

class A extends Activity implements LoaderManager.LoaderCallbacks
{
    @Override
    public void onLoaderReset(Loader loader)
    {
        try
        {
            // Constructs a new Exception that includes the current stack trace.
            throw new Exception();
        }
        catch (Exception e)
        {
            Log.d("TAG", Log.getStackTraceString(e));
        }
    }
}