Android Loaders框架介绍

来源:互联网 发布:sql字符串函数 upper 编辑:程序博客网 时间:2024/05/19 13:17

Loaders框架是在android3.0时推出来的,他能够在activity和fragment中进行异步数据加载。如果你还没有了解或者使用过Loaders

框架,那么强烈建议先阅读一下android官方的Loaders文档:Loaders ,在大致了解之后,按照它上面的例子,亲自写一个小demo。

在用过之后你就会对其有一定的了解。

Loaders框架由LoaderManager,Loader,AsynTaskLoader,CursorLoader类以及LoaderManager.LoaderCallbacks接口组成。

下面我们就根据源码来对Loaders框架进行了解

LoaderManager的源码路径:android-4.0.3_r1\frameworks\base\core\java\android\app\LoaderManager.java

Loader的源码路径:android-4.0.3_r1\frameworks\base\core\java\android\content\Loader.java

AsynTaskLoader的源码路径:android-4.0.3_r1\frameworks\base\core\java\android\content\AsynTaskLoader.java

LoaderManager.LoaderCallbacks接口为LoaderManager的内部接口,路劲与LoaderManager的路径相同。

在分析之前我们首先来回顾一下Loasers框架的使用方法:

1、在Activity或者Fragment中实现LoaderCallbacks。

2、创建继承与抽象类AsyncTaskLoader的类A,在A类的loadInBackground方法中进行数据的异步加载

3、在Activity或者Fragment中的onCreateLoader方法中创建并返回A类的对象

4、在Activity或者Fragment中的onLoadFinished获取加载得到的数据并进行处理

5、所有的都做好了以后,就启动异步数据加载,通过getLoaderManager().initLoader(0, null, this)这段代码开始启动上面那几步的准备。

首先我们来分析LoaderManager的源码:

通过api可以知道LoaderManager其实是一个abstract类,其中包含一个内部接口LoaderCallbacks,LoaderCallbacks源码:

    public interface LoaderCallbacks<D> {        public Loader<D> onCreateLoader(int id, Bundle args);        public void onLoadFinished(Loader<D> loader, D data);        public void onLoaderReset(Loader<D> loader);    }
可以看出其中就只有三个方法。就是我们在第一步中要实现的三个方法。

LoaderManager源码,注意下面只贴出我们可以看到的接口的源码

    public abstract <D> Loader<D> initLoader(int id, Bundle args,            LoaderManager.LoaderCallbacks<D> callback);    public abstract <D> Loader<D> restartLoader(int id, Bundle args,            LoaderManager.LoaderCallbacks<D> callback);    public abstract void destroyLoader(int id);    public abstract <D> Loader<D> getLoader(int id);    public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);    public static void enableDebugLogging(boolean enabled) {        LoaderManagerImpl.DEBUG = enabled;    }
可以看出它其中包含五个抽象方法以及一个静态方法,这六个方法就是LoaderManager所公布出来供我们调用的接口。

通过initLoader方法就能够启动数据的异步加载。其他的几个方法可以通过api去了解,这里我就不解释了。

那么LoaderManger是怎样实现的呢?这个类我们并没有去实现啊,不用着急,在LoaderManager的文件中,她还有一个外部类

LoaderManagerImpl通过方法名我们就能够明白就是这个在同一个文件下的外部类来实现LoaderManager的。接下来我们就来看

其是怎样实现的我们通过initLoader方法来启动异步加载,那么我们就从这个方法开始,看其是怎样启动的:

final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
    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;    }
    private LoaderInfo createLoader(int id, Bundle args,            LoaderManager.LoaderCallbacks<Object> callback) {        LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);        Loader<Object> loader = callback.onCreateLoader(id, args);        info.mLoader = (Loader<Object>)loader;        return info;    }        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;        }    }
    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 activitiy's            // life cycle            info.start();        }    }


SparseArray是一个稀疏数组,他是android中重新开发的一个数组接口,用来替代HashTable在android的使用。具体和HashTable有什么

区别可以查看android的api SparseArray 。

在LoaderManagerImpl内部有一个LoaderInfo内部类,使用mLoader来进行保存,我们知道一个Activity或者Fragment可以开启多个异步加

载任务在initLoader方法中的第一个参数就是用以标记每个Loaders的,然后将每个Loaders的相应信息保存在mLoader中,在调用initLoader

之前,根据id来获取的LoaderInfo肯定为空,那么就会执行createAndInstallLoader方法来创建LoaderInfo对象,在createAndInstallLoader

方法中我们可以看见他调用了createLoader方法来创建LoaderInfo对象,在createLoader方法中我们看到——他根据我们在initLoader方法

中传递进来的id创建了LoaderInfo对象,然后根据initLoader中传递进来的LoaderCallbacks对象,调用了LoaderCallbacks中的

onCreateLoader方法,在Loaders框架的使用步骤中我们知道就是在onCreateLoader方法中返回了异步加载的类的对象的,这样便创建了

异步加载的任务了,在createLoader任务执行完毕后,接下来就执行了installLoader方法,通过这个方法我们看见将LoaderInfo对象以传

递进来的id为主键保存在SparseArray中,在LoaderManagerImpl的构造方法中我们看到mStarted被赋值,也就是Activity或者Fragment启动

时mStarted被赋值,可以猜测到这个值一定是ture因为如果为false的话,那么接下来info.start()这行代码就根本不会执行。那么我就来看

start()这个方法的源码

        void start() {            if (mRetaining && mRetainingStarted) {                mStarted = true;                return;            }            if (mStarted) {                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);                    mListenerRegistered = true;                }                mLoader.startLoading();            }        }

在LoaderInfo中也有一个mStarted标志位,并且没有被赋初始值,在LoaderInfo的构造函数中也没有被赋值,所以刚开始mStarted肯定就

默认为false那么在第一次调用start()方法时,就会执行mStarted被赋值为ture,在上面的createLoader方法中我们看到,调用

onCreateLoader所获取的异步任务对象被传递给了LoaderInfo 中的mLoader了,由于这个异步任务类是继承于AsynTaskLoader,而

AsynTaskLoader是继承与Loader这个抽象类的,故将这个异步任务对象向上强转为Loader对象了,然后赋值给LoaerInfo中的mLoader的

变量了,mListenerRegistered标志位初始值为false,所以调用registerListener方法将这个异步任务的Id以及LoaderInfo对象传递到了

Loader中,然后调用Loader类中的startLoading方法,那么接下来我们就来看startLoading方法的源码

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

在这个方法中我们看见,他调用了onStartLoading方法,而这个方法就是我们实现AsyncTaskLoader类,在异步任务进行前的进行一些准备

工作所要实现的方法。

好了LoaderManager我们就分析道这儿了,解下来我们来分析AsynTaskLoader类:

通过文档以及源码我们可以看到,AsynTaskLoader其实就是AsynTask包了个壳

    final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {        D result;        boolean waiting;        private CountDownLatch done = new CountDownLatch(1);        /* Runs on a worker thread */        @Override        protected D doInBackground(Void... params) {            if (DEBUG) Slog.v(TAG, this + " >>> doInBackground");            result = AsyncTaskLoader.this.onLoadInBackground();            if (DEBUG) Slog.v(TAG, this + "  <<< doInBackground");            return result;        }        /* Runs on the UI thread */        @Override        protected void onPostExecute(D data) {            if (DEBUG) Slog.v(TAG, this + " onPostExecute");            try {                AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);            } finally {                done.countDown();            }        }        @Override        protected void onCancelled() {            if (DEBUG) Slog.v(TAG, this + " onCancelled");            try {                AsyncTaskLoader.this.dispatchOnCancelled(this, result);            } finally {                done.countDown();            }        }        @Override        public void run() {            waiting = false;            AsyncTaskLoader.this.executePendingTask();        }    }

在AsynTaskLoader中我们看到,他有一个内部类LoadTask,而这个就是继承了AsynTask了,然后在AsynTask的doInBackground方法中

调用了AsyncTaskLoader的onLoadInBackground方法,也就是我们在onLoadInBackground中所做的异步操作其实就是在AsynTask的

doInBackground方法中去做的,那么LoadTask对象什么是创建,以及什么时候被执行呢?搜索AsynTaskLoader源码可以发现在他的源码

中就只有一处代码执行executeOnExecutor操作,了解AsynTask就知道这个方法就是执行AsynTask的异步任务。

    void executePendingTask() {        if (mCancellingTask == null && mTask != null) {            if (mTask.waiting) {                mTask.waiting = false;                mHandler.removeCallbacks(mTask);            }            if (mUpdateThrottle > 0) {                long now = SystemClock.uptimeMillis();                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {                    // Not yet time to do another load.                    if (DEBUG) Slog.v(TAG, "Waiting until "                            + (mLastLoadCompleteTime+mUpdateThrottle)                            + " to execute: " + mTask);                    mTask.waiting = true;                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);                    return;                }            }            if (DEBUG) Slog.v(TAG, "Executing: " + mTask);            mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);        }    }

executeOnExecutor方法就是在executePendingTask方法中被调用的,那么我们继续搜索,看executePendingTask方法在那些地方被

调用,发现只有三处:

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

    void dispatchOnCancelled(LoadTask task, D data) {        onCanceled(data);        if (mCancellingTask == task) {            if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!");            mLastLoadCompleteTime = SystemClock.uptimeMillis();            mCancellingTask = null;            executePendingTask();        }    }

        @Override        public void run() {            waiting = false;            AsyncTaskLoader.this.executePendingTask();        }

而我们在实现AsynTaskLoader是在onStartLoading方法中就调用了forceLoad方法,通过这个方法就启动了异步任务了。在异步任务

执行完毕后他会自动执行onPostExecute方法,在这个方法中我们看见他调用了AsynTaskLoader中的dispatchOnLoadComplete

    void dispatchOnLoadComplete(LoadTask task, D data) {        if (mTask != task) {            if (DEBUG) Slog.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 {                mLastLoadCompleteTime = SystemClock.uptimeMillis();                mTask = null;                if (DEBUG) Slog.v(TAG, "Delivering result");                deliverResult(data);            }        }    }

在 dispatchOnLoadComplete我们可以看见他调用了deliverResult方法,就是在这个方法中我们进行处理异步加载所带来的数据。这

样LoaderManager调用initLoader方法启动异步任务,到实际的异步任务是怎样在LoaderManager和AsynTaskLoader被执行,以及

执行结果是怎样最后到deliverResult方法中被处理的。这样整个Loaders框架便介绍完了。

总结:

Loaders框架其实就是AsynTask包了一层壳,他相对与AsynTask的好处我感觉其实就只有一个:那就是当你所加载的数据改变时,

不需要你再次去手动创建AsynTask对象,你只需要调用AsyncTaskLoader的onContentChanged方法就能够实现数据的重

新加载。这就是他的唯一好处。其他的问题AsynTask有他也一样都有,我在网上看见一些人吐槽AsynTask的缺点:

1、cancel()方法在一些情况下失效。在执行doInBackground方法时调用cancel(true)无法打断doInBackground的执行。但是

doInBackground的执行却没有效果。在一些情况下doInBackground的执行是可以打断的,但是如果使用了AsynTask却无法打断。

2、内存泄漏,如果在Activity中使用非静态的匿名内部类的AsynTask,由于java内部类的特点,在Activity关闭时,AsynTask还会持

有外部类Activity的引用,而AsynTask的生命比Activity的长,在Activity关闭后,导致Activity的对象无法被回收。因而产生内存泄漏。

3、结果丢失,我们一般需要AsynTask执行后的结果,然后根据这个结果进行在onPostExecute()方法进行操作,但是如果由于屏幕

转动导致Activity销毁并重新创建后在运行的AsynTask还会持有一个之前Activity的非法引用,导致onPostExecute()没有任何作用。


而转而劝人用Loaders框架,压根就是没看过Loaders框架源码,再次申明一次:AsynTask该有的问题,Loaders框架照样有,唯一

的好处就是我上面说的那一点,其实这一点文档上也说明了。



0 0
原创粉丝点击