Я пытаюсь реализовать свой собственный пользовательский Android Loader, чтобы иметь возможность использовать LoaderManager преимущества в моем приложении (разъединение загрузки данных с жизненным циклом мой Деятельность и Фрагменты).Android Пользовательские Loader, LoaderManagerImpl.LoaderInfo.callOnLoadFinished только раз
Я впервые рассмотрели подклассы от AsyncLoader, но я действительно не нужен загрузка данных, которые будут ставить на AsyncTask (то есть то, что делает AsyncLoader под капотом). Основными данными в моем случае являются/данные/образцы, поступающие из родной библиотеки. Эти образцы обналичены в библиотеке, которая полностью асинхронна по отношению к моим приложениям, поэтому нет необходимости перебирать этот собственный кеш в отдельном потоке.
Вот более или менее, как мой заказ погрузчик выглядит следующим образом:
public class TestSampleListLoader extends Loader<List<TestSample>> {
private static final String TAG = "TestSampleListLoader";
private NativeLibFactory mNativeLib = null;
private SampleReader<TestSample> mTestSampleReader;
private TestSampleListener mTestSampleSampleListener;
private List<TestSample> mTestSampleList;
public TestSampleListLoader(Context context) {
super(context);
Log.i(TAG, "TestSampleListLoader constructor!!!");
}
@Override
public void deliverResult(List<TestSample> testSamples) {
Log.i(TAG, "deliverResult(data) " + testSamples.size());
super.deliverResult(testSamples);
}
@Override
public boolean isStarted() {
Log.i(TAG, "isStarted()");
return super.isStarted();
}
@Override
protected void onStartLoading() {
Log.i(TAG, "onStartLoading()");
super.onStartLoading();
mTestSampleList = new ArrayList<TestSample>();
if (null == mNativeLib) {
initNativeLib();
}
}
@Override
public void forceLoad() {
Log.i(TAG, "forceLoad()");
super.forceLoad();
}
@Override
protected void onForceLoad() {
Log.i(TAG, "onForceLoad()");
super.onForceLoad();
mTestSampleList.clear();
for (TestSample testSample : mTestSampleReader) {
mTestSampleList.add(testSample);
}
Log.i(TAG, "forceLoad(deliverResult) " + mTestSampleList.size());
deliverResult(mTestSampleList);
}
@Override
protected void onReset() {
Log.i(TAG, "onReset()");
mTestSampleList.clear();
if (null != mTestSampleReader) {
mTestSampleReader.close();
mTestSampleReader = null;
}
if (null != mNativeLib) {
mNativeLib.close();
mNativeLib = null;
}
super.onReset();
}
@Override
public void onContentChanged() {
Log.i(TAG, "onContentChanged()");
super.onContentChanged();
}
private void initNativeLib() {
Log.i(TAG, "initNativeLib()");
NativeLibAndroid.initNativeLib(getContext().getApplicationContext(), new NativeLibConnectionListener() {
@Override
public void onNativeLibReady(NativeLibFactory NativeLib) {
Log.d(TAG, "onNativeLibReady!!!");
mNativeLib = NativeLib;
mTestSampleSampleListener = new TestSampleListener();
mTestSampleReader = mNativeLib.createSampleReader(TestSample.class, mTestSampleSampleListener);
}
});
}
public class TestSampleListener implements SampleReaderListener {
@Override
public void onUpdate() {
Log.i(TAG, "TestSampleListener.onUpdate() => onContentChanged");
TestSampleListLoader.this.onContentChanged();
}
}
}
Я использую фрагмент, чтобы отобразить мои родные образцы даты с помощью ArrayAdapter:
public class TestSampleListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<TestSample>> {
private static final String TAG = "TestSampleListFragment";
private static final boolean DEBUG = true;
// The Loader's id (this id is specific to the ListFragment's LoaderManager)
private static final int LOADER_ID = 1;
// We use a custom ArrayAdapter to bind application info to the ListView.
private TestSampleListAdapter mTestReaderAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceSample) {
super.onActivityCreated(savedInstanceSample);
Log.i(TAG, "onActivityCreated()");
mTestReaderAdapter = new TestSampleListAdapter(getActivity());
setEmptyText("No testSamples");
setListAdapter(mTestReaderAdapter);
setListShown(false);
if (DEBUG) {
Log.i(TAG, "Calling initLoader()!");
if (getLoaderManager().getLoader(LOADER_ID) == null) {
Log.i(TAG, "Initializing the new Loader...");
} else {
Log.i(TAG, "Reconnecting with existing Loader (id '1')...");
}
}
// Initialize a Loader with id '1'. If the Loader with this id already
// exists, then the LoaderManager will reuse the existing Loader.
getLoaderManager().initLoader(LOADER_ID, null, this);
}
/**********************/
/** LOADER CALLBACKS **/
/**********************/
@Override
public Loader<List<TestSample>> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader(id) " + id);
// return new TestSampleListLoader(getActivity());
TestSampleListLoaderBis testSampleListLoaderBis = new TestSampleListLoaderBis(getActivity());
return testSampleListLoaderBis;
}
@Override
public void onLoadFinished(Loader<List<TestSample>> loader, List<TestSample> testSampleList) {
Log.i(TAG, "onLoadFinished(): " + testSampleList.size());
setListShown(false);
mTestReaderAdapter.setData(testSampleList);
if (isResumed()) {
Log.i(TAG, "onLoadFinished(isResumed)");
setListShown(true);
} else {
Log.i(TAG, "onLoadFinished(isNotResumed)");
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<List<TestSample>> arg0) {
Log.i(TAG, "onLoaderReset()");
mTestReaderAdapter.setData(null);
}
}
ADB Logcat Следы:
D/TestSampleListLoader(31166): onQeoReady!!!
I/TestSampleListLoader(31166): initQeo(mTestSampleReader): [email protected]
I/TestSampleListLoader(31166): TestSampleListener.onUpdate() => onContentChanged
I/TestSampleListLoader(31166): onContentChanged()
I/TestSampleListLoader(31166): forceLoad()
I/TestSampleListLoader(31166): onForceLoad()
I/TestSampleListLoader(31166): forceLoad(deliverResult) 5
I/TestSampleListLoader(31166): deliverResult(data) 5
I/TestSampleListFragment(31166): onLoadFinished(): 5
I/TestSampleListAdapter(31166): setData(): 5
I/TestSampleListAdapter(31166): setData() for testSample: Test Sample #1
I/TestSampleListAdapter(31166): setData() for testSample: Test Sample #2
I/TestSampleListAdapter(31166): setData() for testSample: Test Sample #3 UPDATED
I/TestSampleListAdapter(31166): setData() for testSample: Test Sample #4
I/TestSampleListAdapter(31166): setData() for testSample: Test Sample #6 UPDATED
I/TestSampleListFragment(31166): onLoadFinished(isResumed)
I/TestSampleListLoader(31166): TestSampleListener.onUpdate() => onContentChanged
I/TestSampleListLoader(31166): onContentChanged()
I/TestSampleListLoader(31166): forceLoad()
I/TestSampleListLoader(31166): onForceLoad()
I/TestSampleListLoader(31166): forceLoad(deliverResult) 5
I/TestSampleListLoader(31166): deliverResult(data) 5
I/TestSampleListLoader(31166): TestSampleListener.onUpdate() => onContentChanged
I/TestSampleListLoader(31166): onContentChanged()
I/TestSampleListLoader(31166): forceLoad()
I/TestSampleListLoader(31166): onForceLoad()
I/TestSampleListLoader(31166): forceLoad(deliverResult) 6
I/TestSampleListLoader(31166): deliverResult(data) 6
Проблема заключается в том, что в настоящее время мой погрузчик правильно информирован об изменениях данных, но только в первый раз, они будут доставлены correclty в сторону LoaderManager.LoaderCallbacksonLoadFinished() обратного вызова. После изменения ориентации это одна и та же история, результат первого раза правильно прибывает в onLoadFinished(), но последующие обновления, поступающие из собственного слоя, не доходят до фрагмента.
Я использовал затмение функции отладки, чтобы отследить проблему и я нашел его в LoaderManager источников (строки 447-453: этот код запускается внутри фрагмента Loader.deliverResult => onLoadComplete [=> callOnLoadFinished => обновить OK]):
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
кажется, что только очень первый раз mData = данные (с mData == NULL в этом случае!). В последующих хитах этого условия mData == данные всегда (и объект данных/массив правильно растет с моим собственным вводом), что очень странно, потому что я не могу выяснить, кто устанавливает/обновляет этот объект mData в пределах LoaderInfo класс в LoaderManagerImpl.
Эта проблема блокирует меня, потому что, только если это условие истинно, выполняется нужный вызов callOnLoadFinished, который будет корректно информировать мой фрагмент и arrayAdapter о базовых изменениях.
Кто-нибудь знает, что я делаю неправильно в моем пользовательском загрузчике, или это пользовательский загрузчик, не очень хорошее решение, когда базовый набор данных меняется все время?
Заранее благодарен! Барт
Хорошая ссылка на Android Пользовательские Погрузчики: http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html
Спасибо за ваш вопрос! Я потратил полдня на борьбу с этим! Не думал, что он сравнивает экземпляры объектов под капотом. –