Глобальный загрузчик (LoaderManager) для повторного использования в нескольких действиях/фрагментах
Что я хотел бы достичь:
У меня есть два разных фрагмента. Я бы хотел, чтобы они оба отображали одни и те же данные в двух формах (в списке и на карте). Я хотел бы, чтобы они делили один загрузчик (в частности, AsyncTaskLoader
). Все работает отлично, но Loader не используется повторно. Создается другая, и данные загружаются дважды.
Что я делаю:
В Fragment
я использую LoaderManager lm = getActivity().getSupportLoaderManager();
В обоих из них я реализую LoaderCallbacks<ArrayList<Item>>
и необходимые методы.
В обоих случаях я использую lm.initLoader(0, args, this);
.
Но когда я вывожу lm.toString()
, кажется, что это два разных загрузчика. И данные загружаются дважды.
Как повторно подключиться к одному загрузчику из другого Activity/Fragment, чем тот, в котором он был запущен?
Это должно быть возможно, поскольку контекст привязан к загрузчику в любом случае на каждом onCreate()
, например. при изменении конфигурации.
Ответы
Ответ 1
Как повторно подключиться к одному загрузчику из другого Activity/Fragment, чем тот, в котором он был запущен?
Вы не должны повторно использовать Loader
, которые управляются экземпляром LoaderManager
через несколько Activity
и Fragment
s.
LoaderManager
запустит/остановит те Loader
по отношению к жизненному циклу Activity
/Fragment
, поэтому нет способа гарантировать, что те Loader
будут существовать, когда вы находитесь в другом Activity
.
Из документации:
LoaderManager.LoaderCallbacks - это интерфейс обратного вызова, который позволяет клиент взаимодействует с LoaderManager.
Ожидается, что загрузчики, в частности CursorLoader, сохранят свои данные после остановки. Это позволяет приложениям сохранять свои данные через действия или фрагменты onStop() и onStart(), поэтому что, когда пользователи возвращаются в приложение, им не нужно ждать данные для перезагрузки. Вы используете методы LoaderManager.LoaderCallbacks когда нужно знать, когда нужно создать новый загрузчик, и сообщить приложение когда пришло время прекратить использование данных загрузчика.
Другими словами, часто бывает, что ваш Loader
будет специфичен для некоторых видов деятельности (или фрагмента). Когда ваш Activity
реализует интерфейс LoaderManager.LoaderCallbacks
, вашей Деятельности присваивается тип LoaderManager.LoaderCallbacks
. Каждый раз, когда вы вызываете initLoader(int ID, Bundle args, LoaderCallbacks<D> callback)
, LoaderManager создает или повторно использует Loader
, специфичный для какого-либо экземпляра интерфейса LoaderManager.LoaderCallbacks
(который в этом случае является экземпляром вашей Activity). Это по существу связывает вашу активность с загрузчиком, и ее методы обратного вызова будут вызваны, когда изменяется состояние загрузчика.
Если вы не можете найти способ, чтобы ваши две отдельные действия использовали одни и те же методы обратного вызова, я сомневаюсь, что есть чистый способ сделать это (то есть, если у Activity и Fragment есть такие же обратные вызовы, как это было бы сложно, если не невозможно). Я бы не стал беспокоиться об этом слишком много. Во всем примере кода, который я когда-либо видел, я никогда не видел, чтобы две операции и/или Фрагменты использовали одни и те же методы обратного вызова. Кроме того, учитывая, что Activity
и Fragment
оба должны быть предназначены для повторного использования, совместное использование Loader
таким образом просто не похоже на то, что можно было бы поощрять.
Ответ 2
Да. Это сработало для меня. У меня есть 3 разных фрагмента в навигационном ящике, где одни и те же данные заполняются в разных списках ListView. (Все фрагменты являются частью ТОЧНОЙ деятельности).
My AsyncTaskLoader:
public class MyTaskLoader extends AsyncTaskLoader<HashMap<String, Integer>> {
public MyTaskLoader(Context context) {
super(context);
}
@Override
public HashMap<String, Integer> loadInBackground() {
...
return hashMap;
}
...
}
Используйте одинаковый идентификатор загрузчика во всех фрагментах.
Fragment1:
public class Fragment1 extends BaseFragment implements LoaderManager.LoaderCallbacks<HashMap<String, Integer>> {
@Override
public void onCreate(Bundle savedInstanceState) {
//initialize adapter
getActivity().getSupportLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<HashMap<String, Integer>> onCreateLoader(int arg0, Bundle arg1) {
// TODO Auto-generated method stub
return new MyTaskLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<HashMap<String, Integer>> arg0,
HashMap<String, Integer> data) {
// TODO Auto-generated method stub
listAdapter.setData(data.keySet());
}
@Override
public void onLoaderReset(Loader<HashMap<String, Integer>> arg0) {
// TODO Auto-generated method stub
listAdapter.setData(null);
}
}
Используйте тот же идентификатор для фрагмента2:
public class Fragment2 extends BaseFragment implements LoaderManager.LoaderCallbacks<HashMap<String, Integer>> {
@Override
public void onCreate(Bundle savedInstanceState) {
//initialize adapter
getActivity().getSupportLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<HashMap<String, Integer>> onCreateLoader(int arg0, Bundle arg1) {
// TODO Auto-generated method stub
return new MyTaskLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<HashMap<String, Integer>> arg0,
HashMap<String, Integer> data) {
// TODO Auto-generated method stub
listAdapter.setData(data.keySet());
}
@Override
public void onLoaderReset(Loader<HashMap<String, Integer>> arg0) {
// TODO Auto-generated method stub
listAdapter.setData(null);
}
}
Адаптер должен быть инициализирован перед инициализацией загрузчика.
Работает до сих пор.
Но правильно ли это? Есть ли лучший метод для использования обычного загрузчика для нескольких фрагментов?
Ответ 3
Я не совсем уверен, что вы пытаетесь архивировать после обсуждения. Но существует метод application.registerActivityLifecycleCallbacks()
, который принимает слушателей жизненного цикла глобальной активности (например, onActivityCreated()
).
Ответ 4
Я думаю, что у вас будет желаемое поведение, если вы используете один и тот же идентификатор загрузчика в разных фрагментах и действиях. Убедитесь, что идентификатор загрузчика уникален для данных, которые нужно загрузить. PhotosLoader и VideoLoader не должны иметь одинаковый идентификатор, например.