AsyncLoaderTask随机停止加载(loadInBackground()停止调用)

时间:2014-03-03 15:30:29

标签: android android-loader

我有一个扩展AsyncTaskLoader并经常接收更新的类。现在,当应用程序最初启动时,一切正常,UI(SherlockListFragment(因此我使用的是兼容性库)实现LoaderManager.LoaderCallbacks<List<Item>>)会在收到更新时进行相应更新。然而,在某些随机点,UI只是停止更新。到目前为止,我没有注意到任何类型的模式,以确定它何时会停止更新;当发生很少的更新或发生许多更新时,可能会发生这种情况。

下面是我的自定义AsyncTaskLoader(我只是将类和变量名称编辑为高度通用的,以便希望使代码更容易理解):

public class CustomLoader extends AsyncTaskLoader<List<Item>> {
private static final String LOG_TAG = "CustomLoader";
private List<Item> items;

public CustomLoader(Context context) {
    super(context);
}

@Override
public List<Item> loadInBackground() {
    return ItemModel.getItemList();
}


@Override
public void deliverResult(List<Item> data) {
    if (isReset()) {
        if (data != null) {
            Log.w(LOG_TAG, "Warning! An async query came in while the Loader was reset!");
            releaseResources(data);
            return;
        }
    }

    // Hold a reference to the old data so it doesn't get garbage collected.
    // We must protect it until the new data has been delivered.
    List<Item> oldItems = items;
    items = data;

    if (isStarted()) {
        Log.i(LOG_TAG, "Delivering results to the LoaderManager.");
        // If the Loader is in a started state, have the superclass deliver the
        // results to the client.
        super.deliverResult(data);
    }

    // Invalidate the old data as we don't need it any more.
    if (oldItems != null && oldItems != data) {
        Log.i(LOG_TAG, "Releasing any old data associated with this Loader.");
        releaseResources(oldItems);
    }
}

@Override
protected void onStartLoading() {
    Log.i(LOG_TAG, "onStartLoading() called!");

    if (items != null) {
        // Deliver any previously loaded data immediately.
        Log.i(LOG_TAG, "Delivering previously loaded data to the client");
        deliverResult(items);
    }

            //Initialises the loader within the model 
    ItemModel.registerLoader(this);

    if (takeContentChanged()) {
        forceLoad();
    }
    else if (items == null) {
        // If the current data is null... then we should make it non-null! :)
        forceLoad();
    }
}

@Override
protected void onStopLoading() {
    Log.i(LOG_TAG, "onStopLoading() called!");

    // The Loader has been put in a stopped state, so we should attempt to
    // cancel the current load (if there is one).
    cancelLoad();
}

@Override
protected void onReset() {
    Log.i(LOG_TAG, "onReset() called!");
    super.onReset();

    // Ensure the loader is stopped.
    onStopLoading();
    // At this point we can release the resources associated.
    if (items != null) {
        releaseResources(items);
        items = null;
    }
    // The Loader is being reset, so we should stop monitoring for changes.
            // We do this by making the loader instance null
    ItemModel.deregisterLoader();
}


@Override
public void onCanceled(List<Item> data) {
    Log.i(LOG_TAG, "onCanceled() called!");

    /**
     * So... we were having problems with the loader sometimes simply not refreshing. It was found
     * that when receiving two updates in quick succession, the loader would call onCanceled() after 
     * the second update (in order to try to stop the previous load). Whenever onCanceled() was called,
     * the loader would stop refreshing. 
     * 
     * And the reason for this?? The support library version of Loader does not support onCanceled() !!!
     * Thanks to this answer on stack overflow for bringing up the issue - http://stackoverflow.com/a/15449553
     * By examining the API for the support library and the API 11 versions of Loader, it is clear that
     * we shouldn't be receiving onCanceled() calls here, but we still do!
     * 
     * Also important to note is that even on Android 3.0 and up, the framework will still use the 
     * support library methods for Loader.
     * 
     * So we simply swallow this onCanceled() call and don't call the super method. This seems to fix 
     * the issue - it may also work if we simply remove onCanceled() completely, but not 100% sure. 
     */

        // Attempt to cancel the current asynchronous load.
    //super.onCanceled(data);

    // The load has been canceled, so we should release the associated resources
            //Uncommenting this line of code does not resolve my issue
    //releaseResources(data);
}

@Override
public void forceLoad() {
    Log.i(LOG_TAG, "forceLoad() called!");
    super.forceLoad();
}

private void releaseResources(List<Item> data) {
    // All resources associated with the Loader should be released here.
    if (data != null) {
        data.clear();
        data = null;
    }
}
}

现在,虽然UI仍在正确更新,但日志显示以下事件序列:

03-03 17:23:33.859: I/CustomLoader(20663): forceLoad() called!
03-03 17:23:33.859: I/CustomLoader(20663): Load in background called...
03-03 17:23:33.864: I/CustomLoader(20663): Delivering results to the LoaderManager.
03-03 17:23:33.864: D/CustomFragment(20663): onLoadFinished() for loader_id 0
03-03 17:23:33.869: I/CustomLoader(20663): Releasing any old data associated with this Loader.

每当数据更新时。

当用户界面停止更新时,似乎forceLoad()每次数据发生变化时都会被调用,但似乎并没有真正完成任何事情(即loadInBackground()没有被调用)。我做了很多研究,看了AsyncTaskLoader的其他实现,我实现的整体逻辑与我发现的所有内容类似,所以我在这里有点亏。

0 个答案:

没有答案