3

Я пытаюсь реализовать свой собственный пользовательский 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

+0

Спасибо за ваш вопрос! Я потратил полдня на борьбу с этим! Не думал, что он сравнивает экземпляры объектов под капотом. –

ответ

1

, если вы хотите, чтобы данные. изменить i wolud удалить mTestSampleList.clear() и заменить его mTestSampleList = new ArrayList();

+0

pskink, это действительно решило проблему! Я думаю, что ясность очищает ссылки только в массиве ArrayList по отношению к отдельным объектам TestSample. Следовательно, с точки зрения ArrayList (и, таким образом, LoaderManager), массив на самом деле не изменяется (размер один и тот же, только ссылки имеют нулевое значение). Отлично! Еще раз спасибо за решение! – Boeboe

Смежные вопросы