Использование сохраненного фрагмента для размещения асинхронных задач не является новой идеей (см. Алекс Локвуд отлично сообщение в блоге по теме)
Но после использования этого я столкнулся с проблемами при доставке контента обратно в свою активность из обратных вызовов AsyncTask. В частности, я обнаружил, что попытка отклонить диалог может привести к исключению IllegalStateException. Опять же, объяснение этого можно найти в еще одно сообщение в блоге от Alex Lockwood. В частности, в этом разделе объясняется, что происходит:
Избегайте выполнения транзакций внутри асинхронных методов обратного вызова.
Это включает обычно используемые методы, такие как AsyncTask # onPostExecute() и LoaderManager.LoaderCallbacks # onLoadFinished(). Проблема с выполнение транзакций в этих методах заключается в том, что они не имеют знание текущего состояния жизненного цикла деятельности, когда они называется. Например, рассмотрим следующую последовательность событий:
- Действие выполняет AsyncTask.
- Пользователь нажимает клавишу "Главная", вызывая действия onSaveInstanceState() и onStop() называться.
- AsyncTask завершается, и вызывается onPostExecute() не подозревая, что деятельность с тех пор была остановлена.
- FragmentTransaction выполняется внутри метода onPostExecute() вызывая исключение.
Однако мне кажется, что это часть более широкой проблемы, просто случается, что диспетчер фрагментов генерирует исключение, чтобы вы знали об этом. В общем, любые изменения, внесенные вами в пользовательский интерфейс после onSaveInstanceState()
, будут потеряны. Поэтому совет
Избегайте выполнения транзакций внутри асинхронных методов обратного вызова.
На самом деле должно быть:
Избегайте выполнения обновлений пользовательского интерфейса внутри асинхронных методов обратного вызова.
Вопросы:
- Если вы используете этот шаблон, вы должны отменить свою задачу, предотвращая обратные вызовы в
onSaveInstanceState()
, если они не вращаются?
Так же:
@Override
public void onSaveInstanceState(Bundle outState)
{
if (!isChangingConfigurations())
{
//if we aren't rotating, we need to lose interest in the ongoing task and cancel it
mRetainedFragment.cancelOnGoingTask();
}
super.onSaveInstanceState(outState);
}
-
Следует ли вообще использовать сохраненные фрагменты для сохранения текущих задач? Будет ли более эффективным всегда отмечать что-то в вашей модели о текущем запросе? Или сделайте что-то вроде RoboSpice, где вы можете повторно подключиться к текущей задаче, если она находится на рассмотрении. Чтобы получить аналогичное поведение с сохраненным фрагментом, вам придется отменить задачу, если вы остановились по причинам, отличным от изменения конфигурации.
-
Продолжая с первого вопроса: даже во время изменения конфигурации вы не должны создавать какие-либо обновления пользовательского интерфейса после
onSaveInstanceState()
, поэтому вы должны сделать что-то вроде этого:
Грубый код:
@Override
public void onSaveInstanceState(Bundle outState)
{
if (!isChangingConfigurations())
{
//if we aren't rotating, we need to lose interest in the ongoing task and cancel it
mRetainedFragment.cancelOnGoingTask();
}
else
{
mRetainedFragment.beginCachingAsyncResponses();
}
super.onSaveInstanceState(outState);
}
@Override
public void onRestoreInstanceState(Bundle inState)
{
super.onRestoreInstanceState(inState);
if (inState != null)
{
mRetainedFragment.stopCachingAndDeliverAsyncResponses();
}
}
beginCachingAsyncResponses()
сделает что-то вроде PauseHandler, увиденного здесь