Вопрос по android – Глобальный загрузчик (LoaderManager) для повторного использования в нескольких действиях / фрагментах

16

What I would like to achieve:

У меня есть два разных фрагмента. Я хотел бы, чтобы они оба показывали одни и те же данные в двух формах (в списке и на карте). Я хотел бы, чтобы они разделили один загрузчик (AsyncTaskLoader особенно). Все работает нормально, но загрузчик не используется повторно. Еще один создан, и данные загружаются дважды.

What I do:

вFragments я используюLoaderManager lm = getActivity().getSupportLoaderManager(); В них обоих я реализуюLoaderCallbacks<ArrayList<Item>> и необходимые методы. В обоих я используюlm.initLoader(0, args, this);.

Но когда я вывожуlm.toString() похоже, что это два разных загрузчика. И данные загружаются дважды.

How to re-connect to the same Loader from a different Activity/Fragment than the one it was started in?

Это должно быть возможно, так как контекст привязан к загрузчику в любом случае на каждомonCreate()например, на изменение конфигурации.

При этом я дам вам возможность объяснить ваши рассуждения, прежде чем я начну звучать слишком педантично: P Alex Lockwood
О нет, вы неправильно поняли. Это не родительская активность! Это совсем другое занятие. Смысл вопроса остался бы прежним, если бы я спросил о двух фрагментах.How to re-use one Loader in two different fragments? Просто я использую Фрагмент внутри некоторой Деятельности для списка и MapActivity для карты, я думал, что, возможно, это имеет значение, но я так не думаю. Michał K
Я подумал, что посещу этот вопрос еще раз, так как он не былcompletely решена. В последние несколько дней я изучал исходный код LoaderManager / Loader, чтобы полностью понять, как он работает, и кажется, что-то подобное не может быть возможным. Каждое действие / фрагмент получает свой собственный LoaderManager (это не глобальный экземпляр), и LoaderManager запускает / останавливает / уничтожает загрузчики по мере необходимости для управления ими на протяжении жизненного цикла действия / фрагмента. Однако вы можете повторно использовать Загрузчики, которыеarent используется с LoaderManager. Подробнее об этом позже (скоро будет новое сообщение в блоге) Alex Lockwood
Можете ли вы объяснить, почему вы должны ссылаться наLoader в обоихFragment и его родительActivity? имейте в виду, что рекомендуется разработать свойFragmentс целью повторного использования ... все может стать довольно быстро, если вы начнете переплетать явное поведение вашегоActivityс иFragments ... попробуйте сделать как можно больше работы в каждом классе отдельно, а затем реализоватьActivity методы обратного вызова при необходимости. Alex Lockwood

Ваш Ответ

3   ответа
1

игации, где одни и те же данные заполняются в разных ListViews. (Все фрагменты являются частью одного и того же действия).

Мой 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);
}
}

Используйте тот же идентификатор для Fragment2:

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);
}
}

Адаптер должен быть инициализирован перед инициализацией загрузчика. Работает до сих пор. Но это правильный путь? Есть ли лучший способ использования общего загрузчика для нескольких фрагментов?

К сожалению, это не сработало. Загрузчик воссоздается только в методе onCreate () каждого фрагмента. Данные загружаются каждый раз, когда создается фрагмент.
Я думаю, что это хорошо - это работает, потому что вы используете (общий) ActivityLoaderManager Activity плюс один и тот же идентификатор загрузчика, так что один и тот же загрузчик просто используется несколько раз. Я не вижу никаких причин, почему это не будет в порядке? И это приятно и просто, нет необходимости кэшировать или создавать другой набор слушателей-вещателей и управлять ими.
0

что у вас будет желаемое поведение, если вы будете использовать один и тот же идентификатор загрузчика для разных фрагментов и действий. Убедитесь, что идентификатор загрузчика уникален для загружаемых данных. Например, PhotosLoader и VideoLoader не должны иметь одинаковый идентификатор.

Я имел в виду Rxjava, но в целом с загрузчиками все в порядке, но есть и лучшие альтернативы. Michał K
Из-за Firebase? Или я что-то упустил? Кстати, была эта статья в этом году:medium.com/google-developers/…
5

t than the one it was started in?

Выshould not повторное использованиеLoaderс которыми управляетLoaderManager экземпляр через несколькоActivityс иFragments.

LoaderManager начнут / остановят теLoaderс относительноActivity/Fragment жизненный цикл, поэтому нет никакого способа гарантировать, что этиLoaders будет существовать, как только вы окажетесь в другомActivity.

Из документации:

LoaderManager.LoaderCallbacks is a callback interface that lets a client interact with the LoaderManager.

Loaders, in particular CursorLoader, are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment's onStop() and onStart() methods, so that when users return to an application, they don't have to wait for the data to reload. You use the LoaderManager.LoaderCallbacks methods when to know when to create a new loader, and to tell the application when it is time to stop using a loader's data.

Другими словами, это часто бывает, что вашLoaders будет специфичным для некоторой деятельности (или фрагмента). Когда у вас естьActivity реализоватьLoaderManager.LoaderCallbacks интерфейс, ваша активность имеет типLoaderManager.LoaderCallbacks, Каждый раз, когда вы звонитеinitLoader(int ID, Bundle args, LoaderCallbacks<D> callback), LoaderManager создает или повторно используетLoader это характерно для некоторого экземпляраLoaderManager.LoaderCallbacks интерфейс (который в данном случае является экземпляром вашей активности). По сути, это связывает вашу активность с загрузчиком, и его методы обратного вызова будут вызываться при изменении состояния загрузчика.

При этом, если вы не можете найти способ, чтобы ваши два отдельных Activity совместно использовали одни и те же методы обратного вызова, я сомневаюсь, что есть чистый способ сделать это (то есть, если Activity и Fragment совместно используют одни и те же обратные вызовы, звучит как сложно если не невозможно). Хотя я бы не слишком волновался об этом. Во всем примере кода, который я когда-либо видел, я никогда не видел, чтобы два действия и / или фрагменты имели одинаковые методы обратного вызова. Далее, учитывая, чтоActivityс иFragmentоба должны быть предназначены для повторного использования, обменаLoaderТаким образом, это не кажется чем-то, что должно поощряться.

С нетерпением жду сообщения тогда :) Кстати, я отказался от идеи и начал использовать ContentProviders, который не отвечает на вопрос, но решил мои проблемы. Michał K
& quot; ... найдите способ, позволяющий двум вашим отдельным действиям использовать одни и те же методы обратного вызова, я сомневаюсь, что есть простой способ сделать это & quot; - Я хотел бы, чтобы кто-то объяснил нам, если это возможно. Из того, что я думаю, это так, потому что это LoaderManager, который связан с жизненным циклом операций / фрагментов, а не LoaderCallbacks. Но, возможно, я ошибаюсь Michał K
Честно говоря, я не думаю, что это то, о чем вам действительно нужно беспокоиться ... это может в конечном итоге принести вам больше хлопот, чем стоит.
Хм ... да, это имеет смысл. Возможно, вы могли бы попробоватьActivitys расширяют класс, который реализуетLoaderManager.LoaderCallbacks, Таким образом, они оба будут наследовать одни и те же обратные вызовы, и, возможно, вы сможете использовать Loader таким образом. Я на самом деле никогда не пытался это сделать ... дайте мне знать, если вы что-нибудь примете, и я обновлю свой ответ :).
Ага! Так что это связано не с Activity, а с LoaderCallbacks. Я не думаю, что было бы невозможно написать внешний класс, который расширяет LoaderCallbacks. Спасибо за это! Вы говорите, что это не поощряется. Но для меня это идеальный способ использовать одни и те же данные, чтобы показать их пользователям двумя различными способами. В списке и на карте в моем примере. Ты не думаешь? Теперь я использую загрузчик, чтобы загрузить его и записываю в контекст приложения. Это не идеально, потому что, если загрузчик в списке не завершил свою работу, и пользователь переключился на карту, то тот же загрузчик запускается снова. Michał K

Похожие вопросы