Чтобы ответить на этот вопрос, вам нужно покопаться в 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вы получите?