Почему 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));
}
}
}