AsynTask异步任务源代码分析

来源:互联网 发布:拳皇13按键优化补丁 编辑:程序博客网 时间:2024/05/14 20:10

AsyncTask异步任务的简单小demo

在Android中很多人都使用过AsyncTask,我们先从下面的一段代码代码来简单熟悉一下异步任务的使用过程吧~~

package com.asyn.task;import android.app.Activity;import android.app.ProgressDialog;import android.os.AsyncTask;import android.os.Bundle;import android.view.View;import android.widget.Button;/** * Created by shion on 2016/7/31. */public class MainActivity extends Activity implements View.OnClickListener {    private Button mButton;    ProgressDialog mDialog;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mButton = (Button) findViewById(R.id.btnProgress);        mButton.setOnClickListener(this);        mDialog = new ProgressDialog(this);        mDialog.setTitle("标题");        mDialog.setMessage("开始下载");        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);        mDialog.setCancelable(false);        mDialog.setCanceledOnTouchOutside(false);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btnProgress:                /**开始异步任务*/                new MyAsynTask().execute(0);                break;        }    }    /**     * 声明AsyntTask的实现类     */    class MyAsynTask extends AsyncTask<Integer, Integer, Integer> {        public MyAsynTask() {            super();        }        /**         * 运行在UI线程,异步任务开始前的回调的方法,这里可以初始化一些基本的数据         */        @Override        protected void onPreExecute() {            mDialog.setMax(100);            mDialog.show();        }         /**         * 运行在子线程,异步任务执行的方法         */        @Override        protected Integer doInBackground(Integer... params) {            /**模拟异步任务耗时的过程*/            while (true) {                try {                    Thread.sleep(500);                } catch (InterruptedException e) {                    e.printStackTrace();                }                params[0] += 10;                if (params[0] == 100) {                    break;                }                /**在执行异步任务的过程中不断更新进度值*/                publishProgress(params[0]);            }            return params[0];        }        /**         * 运行在UI线程,不断更新,通过publishProgress()方法         */        @Override        protected void onProgressUpdate(Integer... values) {            mDialog.setProgress(values[0]);            mDialog.setMessage(values[0] + "/" + 100);        }       /**       *运行在UI线程,异步任务最终执行完毕之后回调       */        @Override        protected void onPostExecute(Integer integer) {            mDialog.setMessage("下载完成!");            mDialog.cancel();        }    }}

代码运行后会有效果如下:
异步任务demo
上面代码运行之后点击Button按钮,会开启一个模拟耗时任务的异步请求的时候主要涉及下面的四个方法:
onPreExecute()方法会进行一些信息初始化,比如这里设置Dialog进度最大值;
doInBackground()会执行耗时操作请求,比如上面模拟耗时加载,实际开发中这里要加载网络请求,
这个方法运行在子线程;
onProgressUpdate()专门用来来更新耗时任务的进度,它是通过publishProgress()方法传值;
onPostExecute是在异步任务执行完成后才在UI中最后调用的方法;
另外,在使用AsyncTask的时候,我们通常需要注意一下几点要求:
1.AsyncTask的初始化必须在UI线程中进行;为什么?
2.AsyncTask的execute()方法也必须要在UI线程中进行;
为什么?
3.AsyncTask的doInBackground()方法运行在子线程里面,所以不能更新UI;为什么?
4.AsynTask的实例对象只允许被执行一次,否则会有异常;
为什么?
带着这些为什么,下面我们从源代码的角度来逐一分析吧!


AsyncTask源代码分析

对于异步任务,我们分别使用Android2.2和Android5.0源代码来分析,这是因为从Android3.0之前和之后AsyncTask的源代码发生了改变,确切的说是改变了对线程池的使用;3.0以前所有任务是并发执行,效率很高,但是有Bug,就是当任务数量超过138个有就会抛异常。3.0以后任务是按顺序执行,效率比之前低了点,但是任务的数量没有限制;下面我们从源代码的角度慢慢看看;

Android2.2 AsyncTask源代码分析


我们首先来看AsyncTask实例对象的源代码new AsyncTask

    /**     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.     */    public AsyncTask() {        mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                return doInBackground(mParams);            }        };        mFuture = new FutureTask<Result>(mWorker) {            @Override            protected void done() {                Message message;                Result result = null;                try {                    result = get();                } catch (InterruptedException e) {                    android.util.Log.w(LOG_TAG, e);                } catch (ExecutionException e) {                    throw new RuntimeException("An error occured while executing doInBackground()",                            e.getCause());                } catch (CancellationException e) {                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));                    message.sendToTarget();                    return;                } catch (Throwable t) {                    throw new RuntimeException("An error occured while executing "                            + "doInBackground()", t);                }                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,                        new AsyncTaskResult<Result>(AsyncTask.this, result));                message.sendToTarget();            }        };    }

这个类有点长,其实就是两个内部类的创建;一个是mWorker实例(Callable类型),一个是mFuture实例(FutureTask类型);
第1-3行是注释,意思是创建见一个异步任务对象,这个构造方法必须执行的UI线程中;
第5-10行是mWorker对象的构造,我们说了,它是一个Callable类型的任务,这点可以从源码中看出

 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {        Params[] mParams;    }

在mWorker中我们重写了它的call方法,当这个任务被执行的时候call()方法会被调用,在哪里?在线程池中,这个后面会知道,再看第8行调用了doInBackGround(),并且返回结果是call方法的返回值;
第12-39行是mFuture对象,它是一个FutureTask类型的实例,并且构造中传入了mWorker.先说说FutureTask是专门用于控制和管理任务的执行,它封装了Callable类型任务(它管理Callable类型的任务),当任务执行的时候,他可以获取任务的执行结果。当前mFurureTask只重写了done()方法,这个方法在Callable类型的任务(mWorker)被取消或者执行完毕的时候,都会被调用的。
上面我们只是大体分析了一些AsyncTask的构造方法里面的初始化的变量信息,至于call()方法和done()方法里面,我们先不管。只看着这么一点我们很难了解真相的,所以在继续往下看execute()方法;


AsyncTask的执行execute()方法的源代码

 public final AsyncTask<Params, Progress, Result> execute(Params... params) {        if (mStatus != Status.PENDING) {            switch (mStatus) {                case RUNNING:                    throw new IllegalStateException("Cannot execute task:"                            + " the task is already running.");                case FINISHED:                    throw new IllegalStateException("Cannot execute task:"                            + " the task has already been executed "                            + "(a task can be executed only once)");            }        }        mStatus = Status.RUNNING;        onPreExecute();        mWorker.mParams = params;        sExecutor.execute(mFuture);        return this;    }

首先这个方法是final修饰,这表明它不允许子类重写这个方法,也就是不能改变异步任务的执行过程;
第2行,mStatus是什么鬼?看看他所属的类,就知道这是一个枚举类,有三个对象,而且mStatus默认是Status.PENDING(待执行状态);

     private volatile Status mStatus = Status.PENDING;     /**     * Indicates the current status of the task. Each status will be set only once     * during the lifetime of a task.     */    public enum Status {        /**         * Indicates that the task has not been executed yet.         */        PENDING,        /**         * Indicates that the task is running.         */        RUNNING,        /**         * Indicates that {@link AsyncTask#onPostExecute} has finished.         */        FINISHED,    }

通过类注释,我们就可以知道这个枚举类是用于指明当前任务的状态,在一个任务的生命周期内每种状态只会被设置一次;这里明显只有三种状态PENDING(任务未执行状态)RUNNING(任务执行状态)FINISHED(任务完成状态)
上面的mStatus默认是Status.PENDING,
所以第2行为false,if条件不会执行~~~
紧接着第14行,mStatus=Status.RUNNING,置为运行状态;
我们在回看第4-12行,就会发现一旦mStatus变为可运行状态之后,便不可以载调用了execute()方法了,否则会有异常,也就说AsynTask的实例对象只允许被执行一次,否则会有异常
第16行,onPreExecute()是一个空方法,需要AsyncTask的子类实现这个方法
第18行,为mWorker的params的属性赋值,记得mWorker是Callable类型的任务;
第19行,sExecutor.execute(mFuture),在线程池sExecutor中执行了任务;
sExecutor是一个线程池,这个我们后面再来分析;
第21行, return返回对象引用本身;

好了,这段代码好像也还是说明不了什么太多的东西,那我们赶紧看看sExecutor.


跟创建sExecutor线程池相关代码

    private static final int CORE_POOL_SIZE = 5;    private static final int MAXIMUM_POOL_SIZE = 128;    private static final int KEEP_ALIVE = 10;    private static final BlockingQueue<Runnable> sWorkQueue =            new LinkedBlockingQueue<Runnable>(10);    private static final ThreadFactory sThreadFactory = new ThreadFactory() {        private final AtomicInteger mCount = new AtomicInteger(1);        public Thread newThread(Runnable r) {            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());        }    };    private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

这部分源代码主要和sExecutor线程池相关,它首先是ThreadPoolExecutor类型的,这个构造中每个参数的含义如下:
CORE_POOL_SIZE:表示的是线程池中核心线程的数量,这里是5个,会一直存活在线程池中,即使空闲状态;
MAXIMUM_POOL_SIZE:表示的是线程池中最大的线程数量这里是128个;
KEEP_ALIVE:当线程池中线程的数量超过了核心线程的数量的时候,KEEP_ALIVE是非核心线程(超出核心线程的那部分)被销毁之前可以等待新任务到来的最大时间;这里显然是指的线程次处于空闲状态的时候;
TimeUnit.SECONDS:是指的KEEP_ALIVE的时间单位,这里是秒;
sWorkQueue:是一个阻塞队列,它的容量为10个;
sThreadFactory:是一个线程工厂,专门用于生产线程,这里生产的线程名称都是AsyncTask#+数字
线程池的这部分就介绍完了,下面看看具体任务是怎么执行的;

这里总结一下,当前线程池最大容量是128,当超过128个的时候,阻塞队列容量是10,再多会怎么办?
再往下面看看线程池的执行就明白了。


线程池的执行 sExecutor.execute(mFuture)
我们要看的是这个方法,线程池的执行,它执行的是线程池中的任务,也就是mFuturn封装的Callable(还记得吗,就是上面最开始的mWorker),就是下面这个代码纵call()方法内部的消息!看到了doInBackground,因为执行在线程池的线程里,所以说doInBackground运行在子线程中,所以说doInBackground()方法中不可以更新UI

 mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                return doInBackground(mParams);            }        };

我们这里不分析线程池的execute方法的内部执行流程了,反正添加到线程池的任务是运行在子线程里面的,我们看下线程池execute方法的注释吧!

 /**     * Executes the given task sometime in the future.  The task     * may execute in a new thread or in an existing pooled thread.     *     * If the task cannot be submitted for execution, either because this     * executor has been shutdown or because its capacity has been reached,     * the task is handled by the current {@code RejectedExecutionHandler}.     *     * @param command the task to execute     * @throws RejectedExecutionException at discretion of     *         {@code RejectedExecutionHandler}, if the task     *         cannot be accepted for execution     * @throws NullPointerException if {@code command} is null     */    public void execute(Runnable command) 

看第5-7行注释:这里说了,如果任务不能被提交执行,要么由于执行已关闭或者线程池的容量已经满了。这时候任务会被RejectedExecutionHandler,也就是会抛出异常。这里也就是说当任务的数量超过线程容量和阻塞队列的容量时,会抛出异常。
开始的时候提到,Android3.0之前的AsyncTask是有bug的,当前线程池容量是128加上阻塞队列是10,也就是138个任务,如果我们一下同时运行超过138个任务的话,马上就会抛异常了。这个地方是AsyncTask异步任务的一个bug,这个bug在Android3.0以后版本被修复了。
再接着call()方法被执行完毕的时候,mFuture就会回调自身的done()方法,这个方法的说明任务已经执行完毕了,我们再看mFuture,

mFuture = new FutureTask<Result>(mWorker) {            @Override            protected void done() {                Message message;                Result result = null;                try {                    result = get();                } catch (InterruptedException e) {                    android.util.Log.w(LOG_TAG, e);                } catch (ExecutionException e) {                    throw new RuntimeException("An error occured while executing doInBackground()",                            e.getCause());                } catch (CancellationException e) {                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));                    message.sendToTarget();                    return;                } catch (Throwable t) {                    throw new RuntimeException("An error occured while executing "                            + "doInBackground()", t);                }                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,                        new AsyncTaskResult<Result>(AsyncTask.this, result));                message.sendToTarget();            }        };

第8行,通过get方法获取到任务的执行结果,也就doBackground()方法的返回值;
第24-26行,通过sHandler发送效果到主线程来更新结果;
message的what是MESSAGE_POST_RESULT
message的Obj是new AsyncTaskResult(AsyncTask.this, result)),这里是使用AsyncTaskResult封装了AsyncTask,和doBackGround的返回值result;

再接着看sHandler

 private static final InternalHandler sHandler = new InternalHandler();    private static class InternalHandler extends Handler {        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})        @Override        public void handleMessage(Message msg) {            AsyncTaskResult result = (AsyncTaskResult) msg.obj;            switch (msg.what) {                case MESSAGE_POST_RESULT:                    // There is only one result                    result.mTask.finish(result.mData[0]);                    break;                case MESSAGE_POST_PROGRESS:                    result.mTask.onProgressUpdate(result.mData);                    break;                case MESSAGE_POST_CANCEL:                    result.mTask.onCancelled();                    break;            }        }    }

这里我们看到了MESSAGE_POST_RESULT,我们顺着这一路慢慢往下看看吧
第6行,先获取AsyncTaskResult的实例对象result,
第10行,result.mTask.finish(result.mData[0]);我们先看看AsyncTaskResult这个类,封装了AsyncTask和泛型T,

 private static class AsyncTaskResult<Data> {        final AsyncTask mTask;        final Data[] mData;        AsyncTaskResult(AsyncTask task, Data... data) {            mTask = task;            mData = data;        }    }

在接着看result.mTask的finish(result.mData[0]),这个finish是怎么实现的?看代码

 private void finish(Result result) {        if (isCancelled()) result = null;        onPostExecute(result);        mStatus = Status.FINISHED;    }

第2行判空,
第3行,onPostExecute(),
第4行, mStatus = Status.FINISHED;标记状态为任务完成状态。

我们再来看 MESSAGE_POST_PROGRESS,它下面执行的是result.mTask.onProgressUpdate(result.mData),它何时会被执行!我们是不是忘了publishProgress方法了,看下面代码,,

  protected final void publishProgress(Progress... values) {        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,                new AsyncTaskResult<Progress>(this, values)).sendToTarget();    }

看看第2行,消息的what等于MESSAGE_POST_PROGRESS,
消息的obj等于new AsyncTaskResult(this, values),然后传递给到sHandler来处理;
到这里,我们就把AsyncTask总体上分析完了。
但是还有两个疑问没有回答呀!AsyncTask为什么必须在UI中创建呀以及它的execute()方法为什么要运行在UI线程中呀?
其实这和Handler的消息机制有关系,如果你不懂的话,你可以看看我的这篇博文消息机制学习一下;
如果在子线程中更新常见的AsyncTask,那么它内部的属性sHandler也就是相当于在子线程中被创建,但是子线程中并没有创建Looper对象呀!这个时候就会抛异常 “Can’t create handler inside thread that has not called Looper.prepare()”;
至于execute()也必须要求在UI线程中执行,就有点牵强了,但是为什么还必须这样要求,是因为onPreExecute()有可能会在UI线程中初始化一些基本信息,比如UI控件相关的,这个时候你必须要让execute()执行在UI主线程中,
而如果onPreExecute()没有实现,那么execute()完全可以在子线程中使用execute方法,不过为了谨慎和安全以及方便,execute()最好不好再子线程中调用;

到这里我们就把AsyncTask都分析完了。上面说到了由于AsyncTask存在缺陷,就是因为线程池的容量问题,导致任务数量不能同时运行超过138个任务,所以Android3.0以后就专门修复了这个BUG,下面我们再来看看Android5.0源码分析


Android5.0源码分析

这里我们就不从头开始分析了,我们只看线程池的处理部分,其他都是差不多,5.0中把任务按照顺序来执行,而3.0之前是并发执行的,我们还是从源码的角度分析吧5.0中AsyncTask部分含有2个线程池,一个用于顺序控制,一个用于执行任务。看看源代码,这个设计真的很精妙,就是执行效率比之前慢了

  private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;   private static class SerialExecutor implements Executor {        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();        Runnable mActive;        public synchronized void execute(final Runnable r) {            mTasks.offer(new Runnable() {                public void run() {                    try {                        r.run();                    } finally {                        scheduleNext();                    }                }            });            if (mActive == null) {                scheduleNext();            }        }        protected synchronized void scheduleNext() {            if ((mActive = mTasks.poll()) != null) {                THREAD_POOL_EXECUTOR.execute(mActive);            }        }    }

第6行,当执行任务的时候,首先会创建一个任务,添加到mTask顺序队列里面;
第16行,第一次是null,if条件为true,进入scheduleNext()
第22行,mTask.poll()去任务,是非空的,
第23行,THREAD_POOL_EXECUTOR是另外一个线程池,专门用于执行,不贴代码了,开始执行任务了。
再看第10行,mFutrueTask调用自身的run()方法,最后还是会执行mWorker,这里这个不是重点
5.0以后AsyncTask内部开始自己定义了一个线程池SERIAL_EXECUTOR,内部阻塞队列换成了ArrayDeque,同时execute方法变成了synchorzied的了,这样即使在不同的线程中,执行的时候就是在这个队列里面同步取任务让另一个线程池执行,方法变成同步有序的了,而且队列还是无容量限制的,只是执行任务是按照顺序同步执行,效率赶不上3.0之前的异步执行。但是解决了之前的BUG。

到这里消息机制就分析就到这里了,有不对的地方希望多多指正,相互学习,共同进步~~

2 0