Чтобы ответить на этот вопрос, вам нужно покопаться в LoaderManager
коде. Хотя документация для самого LoaderManager недостаточно ясна (иначе бы не было этого вопроса), документация для LoaderManagerImpl, подкласса абстрактного LoaderManager, гораздо более информативна.
initLoader
Вызов для инициализации конкретного идентификатора с помощью загрузчика. Если с этим идентификатором уже связан загрузчик, он остается неизменным, а все предыдущие обратные вызовы заменяются вновь предоставленными. Если в настоящее время загрузчика для идентификатора нет, создается и запускается новый.
Эту функцию обычно следует использовать при инициализации компонента, чтобы гарантировать создание загрузчика, на который он полагается. Это позволяет повторно использовать данные существующего загрузчика, если он уже есть, так что, например, когда действие создается повторно после изменения конфигурации, ему не нужно повторно создавать свои загрузчики.
restartLoader
Вызов для воссоздания загрузчика, связанного с определенным идентификатором. Если в настоящее время с этим идентификатором связан загрузчик, он будет отменен / остановлен / уничтожен соответствующим образом. Будет создан новый загрузчик с заданными аргументами, и его данные будут доставлены вам, когда они станут доступны.
[...] После вызова этой функции любые предыдущие загрузчики, связанные с этим идентификатором, будут считаться недействительными, и вы не будете получать от них дальнейшие обновления данных.
В основном есть два случая:
- Загрузчика с идентификатором не существует: оба метода создадут новый загрузчик, поэтому разницы нет.
- Загрузчик с идентификатором уже существует:
initLoader
заменит только обратные вызовы, переданные в качестве параметра, но не отменит или остановит загрузчик. Для a CursorLoader
это означает, что курсор остается открытым и активным (если это было до initLoader
вызова). `restartLoader, с другой стороны, отменит, остановит и уничтожит загрузчик (и закроет базовый источник данных, например, курсор) и создаст новый загрузчик (который также создаст новый курсор и повторно запустит запрос, если загрузчик CursorLoader).
Вот упрощенный код для обоих методов:
initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
// Loader doesn't already exist -> create new one
info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
// Loader exists -> only replace callbacks
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
restartLoader
LoaderInfo info = mLoaders.get(id);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
if (inactive != null) {
// does a lot of stuff to deal with already inactive loaders
} else {
// Keep track of the previous instance of this loader so we can destroy
// it when the new one completes.
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Как мы видим, если загрузчик не существует (info == null), оба метода создадут новый загрузчик (info = createAndInstallLoader (...)). В случае, если загрузчик уже существует, initLoader
только заменяет обратные вызовы (info.mCallbacks = ...), при этом restartLoader
деактивирует старый загрузчик (он будет уничтожен, когда новый загрузчик завершит свою работу), а затем создает новый.
Таким образом, теперь ясно, когда использовать, initLoader
а когда использовать, restartLoader
и почему имеет смысл иметь два метода.
initLoader
используется для проверки наличия инициализированного загрузчика. Если такового не существует, создается новый, если он уже существует, он используется повторно. Мы всегда используем этот метод, ЕСЛИ нам не нужен новый загрузчик, потому что запрос для выполнения изменился (не базовые данные, а фактический запрос, как в операторе SQL для CursorLoader), и в этом случае мы будем вызывать restartLoader
.
Деятельность / Фрагмент жизненного цикла не имеет ничего общего с решением использовать один или другой метод (и нет необходимости отслеживать звонки , используя флаг один выстрел , как предположил Симон)! Это решение принято исключительно исходя из «необходимости» нового погрузчика. Если мы хотим выполнить тот же запрос, который мы используем initLoader
, если мы хотим выполнить другой запрос, который мы используем restartLoader
.
Мы всегда могли использовать, restartLoader
но это было бы неэффективно. После поворота экрана или если пользователь уходит от приложения и позже возвращается к тому же самому Activity, мы обычно хотим показать тот же результат запроса, и поэтому restartLoader
без необходимости повторно создаст загрузчик и отклонит базовый (потенциально дорогой) результат запроса.
Очень важно понимать разницу между загружаемыми данными и «запросом» для загрузки этих данных. Предположим, мы используем CursorLoader, запрашивающий у таблицы заказы. Если в эту таблицу добавляется новый порядок, CursorLoader использует onContentChanged (), чтобы информировать пользовательский интерфейс об обновлении и отображении нового порядка ( restartLoader
в этом случае нет необходимости ). Если мы хотим отображать только открытые ордера, нам нужен новый запрос, и мы должны использовать его restartLoader
для возврата нового CursorLoader, отражающего новый запрос.
Есть ли какая-то связь между двумя методами?
Они совместно используют код для создания нового загрузчика, но они делают разные вещи, когда загрузчик уже существует.
Есть ли вызов restartLoader
всегда позвонить initLoader
?
Нет, никогда не бывает.
Могу ли я позвонить, restartLoader
не звоня initLoader
?
Да.
Безопасно ли initLoader
дважды звонить для обновления данных?
initLoader
Дважды позвонить безопасно, но данные не будут обновлены.
Когда мне следует использовать один из двух и почему ?
Это должно (надеюсь) быть ясным после моих объяснений выше.
Изменения конфигурации
LoaderManager сохраняет свое состояние при изменении конфигурации (включая изменение ориентации), поэтому вы можете подумать, что нам больше нечего делать. Подумай еще раз ...
Во-первых, LoaderManager не сохраняет обратные вызовы, поэтому, если вы ничего не сделаете, вы не получите вызовов к своим методам обратного вызова onLoadFinished()
и т. Д., И это, скорее всего, сломает ваше приложение.
Поэтому мы ДОЛЖНЫ вызвать хотя бы initLoader
для восстановления методы обратного вызова (это restartLoader
, конечно, тоже возможно). В документации указано:
Если в момент вызова вызывающая сторона находится в своем запущенном состоянии, а запрошенный загрузчик уже существует и сгенерировал свои данные, то обратный вызов onLoadFinished(Loader, D)
будет вызван немедленно (внутри этой функции) [...].
Это означает, что если мы позвоним initLoader
после изменения ориентации, мы сразу же получим onLoadFinished
вызов, потому что данные уже загружены (при условии, что это было до изменения). Хотя это звучит прямо, но может быть сложно (разве мы не все любим Android ...).
Мы должны различать два случая:
- Самостоятельно обрабатывает изменения конфигурации: это относится к фрагментам, использующим setRetainInstance (true), или к Activity с соответствующим
android:configChanges
тегом в манифесте. Эти компоненты не получат вызов onCreate, например, после поворота экрана, поэтому не забывайте вызывать
initLoader/restartLoader
другой метод обратного вызова (например, in
onActivityCreated(Bundle)
). Чтобы иметь возможность инициализировать загрузчик (-ы), идентификаторы загрузчика должны быть сохранены (например, в списке). Поскольку компонент сохраняется при изменении конфигурации, мы можем просто перебрать существующие идентификаторы загрузчика и вызвать initLoader(loaderid,
...)
.
- Сам не обрабатывает изменения конфигурации: в этом случае загрузчики могут быть инициализированы в onCreate, но нам нужно вручную сохранить идентификаторы загрузчиков, иначе мы не сможем сделать необходимые вызовы initLoader / restartLoader. Если идентификаторы хранятся в ArrayList, мы должны выполнить
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
onSaveInstanceState и восстановить идентификаторы в onCreate:
loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey)
перед тем, как сделать вызов (ы) initLoader.
initLoader
(и все обратные вызовы завершены, загрузчик простаивает) после ротации, вы не получитеonLoadFinished
обратный вызов, но если вы используете,restartLoader
вы получите?