Android loders异步加载框架

Loader基本介绍

  • Loader:是一个执行异步数据加载的抽象类。它是AsyncTaskLoader的父类,CursorLoader的祖父类。
  • AsyncTaskLoader:它是继承于Loader的抽象类。在AsyncTaskLoader中有AsyncTask用来执行异步工作。
  • CursorLoader:它是AsyncTaskLoader的子类。它可以查询ContentResolver然后返回一个Cursor,同时CursorLoader内包含ContentObserver对象来监听Cursor数据的变化。
  • LoaderManager:它是一个抽像类,作用是用来管理Loader。LoaderManager关联到一个Activity或Fragment;例如,在Activity中通过getLoaderManager()可以获取到一个LoaderManager对象。
  • LoaderManager.LoaderCallbacks:它提供了与"Activity或Fragment"交互的接口。LoaderCallbacks中共包行三个接口:
    • onCreateLoader():根据所给出的ID,初始化并返回一个新的加载器。
    • onLoadFinished():当一个先前被创建的加载器完成了它的加载过程时被调用。
    • onLoaderReset():当一个先前被创建的加载器被重置时被调用,然后使加载器的数据无效。

总结:Loader, AsyncTaskLoader和CursorLoader都是用来提供异步加载的类;而LoaderManager则提供了管理这些异步加载类的接口。此外,Activity或Fragment保护了获取LoaderManager的接口,通过这样的接口,就可以在Activity或Fragment中实现异步加载任务!

基本使用

启动装载器

通常要在Activity的onCreate()方法中或Fragment的onActivityCreated()方法中初始化一个装载器:

getLoaderManager().initLoader(0, null, this);
  • id:一个唯一的ID用于标识这个装载器,同一个id的装载器可以做到重复利用;
  • args:可选的参数,在装载器初始化时作为参数传入;参数经过流转,最终会传到onCreateLoader(int id, Bundle args)
  • callbacks:一个LoaderManager.LoaderCallbacks的实现,被LoaderManager调用以报告装载器事件;
源码分析
public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
        if (mCreatingLoader) {
            throw new IllegalStateException("Called while creating a loader");
        }

        LoaderInfo info = mLoaders.get(id);

        if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);

        if (info == null) {
            // Loader doesn't already exist; create.
            info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
            if (DEBUG) Log.v(TAG, "  Created new loader " + info);
        } else {
            if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);
            info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
        }

        if (info.mHaveData && mStarted) {
            // If the loader has already generated its data, report it now.
            info.callOnLoadFinished(info.mLoader, info.mData);
        }

        return (Loader<D>)info.mLoader;
    }

主要是两件事情:

  1. 根据传入的id,查询到对应的LoaderInfo数据;
  2. 如果查询的数据为空,表示装载器未创建,则需要创建装载器;
  3. 如果查询的数据不为空,则只是更新下callback,然后直接调用onLoadFinished()。

下面是loadFinished的代码逻辑:

void callOnLoadFinished(Loader<Object> loader, Object data) {
            if (mCallbacks != null) {
                String lastBecause = null;
                if (mHost != null) {
                    lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
                    mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
                }
                try {
                    if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "
                            + loader.dataToString(data));
                    mCallbacks.onLoadFinished(loader, data);
                } finally {
                    if (mHost != null) {
                        mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
                    }
                }
                mDeliveredData = true;
            }
        }

如果没有创建,则需要创建。

创建的逻辑
private LoaderInfo createAndInstallLoader(int id, Bundle args,
            LoaderManager.LoaderCallbacks<Object> callback) {
        try {
            mCreatingLoader = true;
            LoaderInfo info = createLoader(id, args, callback);
            installLoader(info);
            return info;
        } finally {
            mCreatingLoader = false;
        }
    }

private LoaderInfo createLoader(int id, Bundle args,
            LoaderManager.LoaderCallbacks<Object> callback) {
        LoaderInfo info = new LoaderInfo(id, args,  callback);
        Loader<Object> loader = callback.onCreateLoader(id, args);
        info.mLoader = loader;
        return info;
    }  

void installLoader(LoaderInfo info) {
        mLoaders.put(info.mId, info);
        if (mStarted) {
            // The activity will start all existing loaders in it's onStart(),
            // so only start them here if we're past that point of the activity's
            // life cycle
            info.start();
        }
    }

下面进入到LoaderManager.LoaderInfo里面:

void start() {
            if (mRetaining && mRetainingStarted) {
                // Our owner is started, but we were being retained from a
                // previous instance in the started state...  so there is really
                // nothing to do here, since the loaders are still started.
                mStarted = true;
                return;
            }

            if (mStarted) {
                // If loader already started, don't restart.
                return;
            }

            mStarted = true;

            if (DEBUG) Log.v(TAG, "  Starting: " + this);
            if (mLoader == null && mCallbacks != null) {
               mLoader = mCallbacks.onCreateLoader(mId, mArgs);
            }
            if (mLoader != null) {
                if (mLoader.getClass().isMemberClass()
                        && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
                    throw new IllegalArgumentException(
                            "Object returned from onCreateLoader must not be a non-static inner member class: "
                            + mLoader);
                }
                if (!mListenerRegistered) {
                    mLoader.registerListener(mId, this);
                    mLoader.registerOnLoadCanceledListener(this);
                    mListenerRegistered = true;
                }
                mLoader.startLoading();
            }
        }

主要是3个功能:

  1. 注册完成的监听
  2. 注册取消的监听
  3. 触发加载

-> Loader.startLoading:

public final void startLoading() {
        mStarted = true;
        mReset = false;
        mAbandoned = false;
        onStartLoading();
    }

protected void onStartLoading() {
}

而onStartLoading方法是给实际的集成者完成的,比如我这边自己实现的Loader类:

@Override
    protected void onStartLoading() {
        if (netBean != null) {
            deliverResult(netBean);
        } else {
            forceLoad();
        }
    }

其中deliverResult()forceLoad() 都是Loader类里的方法。

public void deliverResult(D data) {
        if (mListener != null) {
            mListener.onLoadComplete(this, data);
        }
    }

public void forceLoad() {
        onForceLoad();
    }

protected void onForceLoad() {
    }

其中onForceLoadAsyncTaskLoader类实现,该类是基于AsyncTask实现的一个异步加载框架。

@Override
    protected void onForceLoad() {
        super.onForceLoad();
        cancelLoad();
        mTask = new LoadTask();
        if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
        executePendingTask();
    }

异步加载器如下:

@Override
        protected D doInBackground(Void... params) {
            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
            try {
                D data = AsyncTaskLoader.this.onLoadInBackground();
                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
                return data;
            } catch (OperationCanceledException ex) {
                if (!isCancelled()) {
                    throw ex;
                }
                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
                return null;
            }
        }

        /* Runs on the UI thread */
        @Override
        protected void onPostExecute(D data) {
            if (DEBUG) Log.v(TAG, this + " onPostExecute");
            try {
                AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
            } finally {
                mDone.countDown();
            }
        }

        /* Runs on the UI thread */
        @Override
        protected void onCancelled(D data) {
            if (DEBUG) Log.v(TAG, this + " onCancelled");
            try {
                AsyncTaskLoader.this.dispatchOnCancelled(this, data);
            } finally {
                mDone.countDown();
            }
        }
  1. onLoadInBackground

    public abstract D loadInBackground();
    
    protected D onLoadInBackground() {
         return loadInBackground();
     }
    

    上面的抽象类是需要子类实现的,如我的demo:

    @Override
     public NetBean loadInBackground() {
         Log.e("111", "=======================");
         try {
             Thread.sleep(2000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
    
         netBean = new NetBean();
         return netBean;
     }
    
  2. dispatchOnLoadComplete
    void dispatchOnLoadComplete(LoadTask task, D data) {
         if (mTask != task) {
             if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
             dispatchOnCancelled(task, data);
         } else {
             if (isAbandoned()) {
                 // This cursor has been abandoned; just cancel the new data.
                 onCanceled(data);
             } else {
                 commitContentChanged();
                 mLastLoadCompleteTime = SystemClock.uptimeMillis();
                 mTask = null;
                 if (DEBUG) Log.v(TAG, "Delivering result");
                 deliverResult(data);
             }
         }
     }
    
    最终也是走到Loader的deliverResult()方法。最终走到onLoadFinished().
  3. dispatchOnCancelled

    void dispatchOnCancelled(LoadTask task, D data) {
         onCanceled(data);
         if (mCancellingTask == task) {
             if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
             rollbackContentChanged();
             mLastLoadCompleteTime = SystemClock.uptimeMillis();
             mCancellingTask = null;
             if (DEBUG) Log.v(TAG, "Delivering cancellation");
             deliverCancellation();
             executePendingTask();
         }
     }   
    
    public void deliverCancellation() {
         if (mOnLoadCanceledListener != null) {
             mOnLoadCanceledListener.onLoadCanceled(this);
         }
     }
    

重启装载器

有的时候你却想丢弃旧的装载器然后开始一个新的装载器,要想丢弃旧的装载器,你应使用restartLoader()

重启装载器的实现,主要是先将老的装载器查询出来,释放掉所有的资源,然后重新创建。

LoaderManager.LoaderCallbacks

装载器的回调接口,实现应用层与LoaderManager的交互,主要是如下几个回调:

  • onCreateLoader():根据传入的ID,初始化并返回一个新的装载器
  • onLoadFinished():当一个装载器完成了它的装载过程后调用
  • onLoaderReset():当一个装载器被重置,从而使得其数据无效时被调用

自定义Loader

/*
 * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
 */
package com.pan.lean.photowall.loaders;

import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;

/**
 * Created by panhongchao on 17/3/20.
 */
public class NetworkLoader extends AsyncTaskLoader<NetBean> {
    private NetBean netBean;

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

    @Override
    public NetBean loadInBackground() {
        try {
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.e("111", "loader loadInBackground is called");

        netBean = new NetBean();
        return netBean;
    }

    @Override
    protected void onStartLoading() {
        if (netBean != null) {
            deliverResult(netBean);
        } else {
            forceLoad();
        }
    }


    @Override
    protected void onStopLoading() {
        cancelLoad();
    }

    @Override
    public void onCanceled(NetBean data) {
        netBean = null;
    }
}

Loader框架与AsyncTask

Loader框架最大的改进是异步的线程的加载跟Fragment/Activity的生命周期绑定上了,从而从根本上保证了内存泄漏的问题。

这是怎么实现的呢?

是通过将F/A的生命周期的每次回调都代理到了LoaderManager上。

以Destory为例:

results matching ""

    No results matching ""