3

我正在尝试实现我自己的自定义 Android加载器,以便能够在我的应用程序中使用LoaderManager的好处(将加载数据与我的活动片段的生命周期解耦)。

我首先考虑了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();
        }
    }
}

我正在使用Fragment通过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): org.qeo.internal.SampleReaderImpl@41d29e68

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

现在的问题是,我的Loader已正确通知数据更改,但仅在第一次将它们正确传递给LoaderManager.LoaderCallbacks onLoadFinished()回调时。在方向改变之后,情况相同,第一次结果正确到达onLoadFinished(),但来自本机层的后续更新没有到达片段。

我使用 eclipse 调试功能来追踪问题,并在LoaderManager源代码中找到了它(第 447-453 行:此代码是从 Loader.deliverResult => onLoadComplete [=> callOnLoadFinished => fragment update 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 != data(因为mData == null在这种情况下)。在此条件的后续命中中,mData == data始终(并且数据对象/数组随着我的本机输入正确增长),这很奇怪,因为我无法找出谁在LoaderInfo类中设置/更新此 mData 对象LoaderManagerImpl。_

这个问题阻碍了我,因为只有当这个条件为真时,才会完成对callOnLoadFinished的必要调用,这将正确地通知我的片段和 arrayAdapter 底层更改。

任何人都知道我在自定义加载器中做错了什么,或者当底层数据集一直在变化时,自定义加载器不是一个很好的解决方案?

提前致谢!巴特

Android 自定义加载器的良好参考: http ://www.androiddesignpatterns.com/2012/08/implementing-loaders.html

4

1 回答 1

1

如果你想要数据。更改我将删除 mTestSampleList.clear() 并将其替换为 mTestSampleList = new ArrayList();

于 2013-10-27T14:06:56.660 回答