Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android 异步任务:AsyncTask机制 源码详细版解析------从入门到升天

$
0
0

相信大家对AsyncTask机制都不陌生?基本的使用的方法和注意事项都清楚,编写一个类,继承AsyncTask类,必须重写doInBackground()方法,运行在子线程,耗时的操作在此执行;onPostExecute()方法在doInBackground()执行完被回调,运行在主线程,可进行UI更新;若需要显示更新的进度,可重写onProgressUpdate()方法。

在一开始学习异步任务AsyncTask时,我们就在被各种教程传输这些理论知识,确实对于它已经了然于心,可是有考虑过底层是如何详细实现AsyncTask机制的吗?为何onPreExecute()方法最先被调用doInBackground()如何封装子线程中的?onPostExecute方法回调又是如何实现的?这些方法之间是如何被联系起来的?

虽然Android已为我们封装AsyncTask类,只有从源代码的角度去剖析才能真正掌握。(以下讲解会一步步深入,剥茧抽丝,弄清楚详细实现方式,想要彻底剖析一定要有耐心,另配有逻辑图供大家整理AsyncTask机制的逻辑流程!)



一. AsyncTask机制

1. AsyncTask基本使用:

AsyncTask<String, Integer, Object> asyncTask = new AsyncTask<String, Integer, Object>() {

    protected void onPreExecute() {};

    @Override
    protected Object doInBackground(String... params) {
        return null;
    }

    protected void onPostExecute(Object result) {};

    protected void onProgressUpdate(Integer[] values) {};

};
asyncTask.execute("params")

问题:

  1. onPreExecute() 方法为何在主线程中执行?为何最先执行?
  2. doInBackground()方法为何在子线程中执行?如何实现该方法执行完就回调 onPostExecute() 方法?
  3. onPostExecute() 方法为何在主线程中进行?

以上为AsyncTask的基本使用:new 一个AsyncTask类,重写需要逻辑实现的方法。调用execute方法,任务就开始执行了。所以,看源代码就从这个入口execute方法开始分析:






AsyncTask的execute方法

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //(1)检测状态
    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)");         
        }
    }
    //(2)修改状态
    mStatus = Status.RUNNING;

    //(3)调用onPreExecute方法
    onPreExecute();

    //(4)把参数赋值给mWorker对象
    mWorker.mParams = params;

    //(5)线程池对象执行mFuture
    sExecutor.execute(mFuture);

    return this;
}

在此方法体中,开始执行异步任务,逐步骤分析:


(1)检测状态

若状态为RUNNING:顾名思义为正在执行,抛出异常“无法执行,该任务正在执行”。所以说一旦new 了一个AsyncTask连续调用两次execute方法,肯定会报错。

若状态为FINISHED:顾名思义为已经执行过,抛出异常“无法执行,该任务已经执行过,一个任务只能执行一次”。相当于AsyncTask类中的doInBackground方法已经执行完,回调过onPostExecute方法了,再次调用execute方法,比如想再执行一次下载任务,就会报错!

由以上我们可以得出一个结论:execute方法只能调用一次,若想多次执行,多次的new一个AsyncTask类,再去调用对应的execute方法。



(2)修改状态

在进过步骤一后(代表状态判断未报错),即可修改状态为RUNNING(即该任务正在执行)。



(3)调用onPreExecute方法

看到onPreExecute方法是不是觉得倍感亲切,虽然我们在new一个AsyncTask重写其方法时,很少会去重写该方法。现在我们知道此方法为何是在主线程中执行的了:

因为最开始new一个AsyncTask后,执行execute方法这些步骤本就是在主线程中执行的,而execute方法中又调用onPreExecute方法,所以自然其运行在主线程,并且也是以上四个方法中最先执行的方法!


【AsyncTask机制逻辑流程图 v1】:
这里写图片描述



(4)把参数赋值给mWorker对象

mWorker.mParams = params;

以上代码中的params就是 一开始调用AsyncTaskexecute方法时传进来的参数,此参数被保存到mWorker对象中的mParams变量中,可是这个mWorker又是什么?首先来看它的声明

privaet final WorkerRunnable<Params , Result> mWorker;

mWorker是一个WorkerRunnable类,继续深入,查看其具体实现

private static class InternalHandler extends Handler{
    @Override
    public void handleMessage(MMessage msg){
        ...//我也很重要,后面再讲
    }

    //☆☆☆看我☆☆☆
    private static abstact class WorkerRunnable<Params, Result> implements Callable<Result>{
        Params[] mParams;
    }
    ...
}

以上可得知,WorkerRunnable是一个InternalHandler 类(重要的一个类)的内部类,有一个mParams数组,来保存了一开始调用AsyncTaskexecute方法时传进来的参数


【AsyncTask机制逻辑流程图 v2】:
这里写图片描述


继续分析,此类还实现了一个Callable接口,查看具体实现

public interface Callable<V>{
        V call() throws Exception;
}

这个Callable接口中只有一个call()方法,方法作用?何时调用?先留下一个疑问,我们后文分解。既然”mWorker对象的声明“这一个线索已经挖到底了,就寻找下一个突破口,查看它的初始化

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){
        ...//我也很重要,后面再讲
    }
}

mWorker初始化部分在AsyncTask的构造方法中,代表最开始new 一个 AsyncTask时,mWorker对象就已经被初始化了。以上分析,其实mWorker就是一个Callable类型对象,它在初始化时new完后,覆盖了一个call方法,而call方法的返回值正是 调用doInBackground()方法的返回值,并且在调用的时候传递了参数mParams


【AsyncTask机制逻辑流程图 v3】:
这里写图片描述


现在问题二就来了,这个doInBackground()方法是如何运行在子线程中?也就是说这个call方法何时被一个子线程调用?

莫方!先意识到这个问题,保留疑问,继续往下走。以上就是步骤四的逻辑,我们继续回到execute方法中,依次执行步骤五,来解决疑问。



(5)线程池对象执行mFuture

sExecutor.execute(mFuture);

老样子!这一行代码看不懂,首先查找sExecutor对象声明

private static final ThreadPool Executor sExecutor = new ThreadPoolsExecutor(CORE_POOL_SIZE,MAXIMUN_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sWorkQueue,sThreadFactory);

真面目揭晓!所以说Asynctask能够执行多个任务,是因为类中有一个线程池回到步骤五的那行代码中,sExecutor对象执行execute方法就是往线程池中取出来一个线程,去处理括号中的内容(即mFuture),mFuture又是何物?来查看声明

FutureTask<Result> mFuture;

得知mFuture是一个FutureTask类型,继续深入,查看具体实现

public class FutureTask<V> implements RunnableFuture<V>{
    public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
        sync = new Sync(callable);
    }
    ...
}

发现FutureTask类型又是实现了RunnableFuture,剥茧抽丝,继续深入查看具体实现

public interface RunnableFuture<V> extends Runnable,Future<V>{
    void run();
}

可以恍然大悟了!原来这个RunnableFuture接口是实现了Runnable,所以里面就有一个run方法,这跟我们所学习的理论知识“运行子线程时一般要在run方法中执行”是非常吻合的!理解之后,这一条“mFuture的声明”线索也到底了,注意需要明确一点:执行mFuture对象就是在执行run方法!现在问题就是这个run方法在哪里被调用了?或者说mFuture在哪里覆盖重写run方法?寻找新线索,mFuture声明完后,来查找它初始化的地方:

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){
    //此处重写done方法
    @Override
    protected void done() {
        ...
        //获取doInbackground方法返回的结果
        result = get();
        ...
        //创建一个消息
        message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(AsyncTask.this, result));
        //把这条消息发送给创建这个消息的Handler:target.sendMessage(this)
        message.sendToTarget();
    }
    };
}

找到mFuture初始化地方,乍一看到步骤四中分析的mWorker对象也是在 AsyncTask的构造方法中(即AsyncTasknew出来的时候这两个对象就都实例化了!)。并且mFuturenew 一个 FutureTask时,传了一个参数mWorker。(这里简单回顾mWorker是一个Callable对象,有一个call方法,方法中调用doInBackground),代表将参数mWorker存储FutureTask类的构造方法中了,继续深入,查看FutureTask实现

public class FutureTask<V> implements RunnableFuture<V>{
    private final Sync sync;

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
            sync = new Sync(callable);
    }
} 

查看FutureTask构造方法,也可以证实之前对mWorker对象的回顾,就是一个Callable类型。sync = new Sync(callable);又把mWorker封装Sync对象Sync为何物?查看其具体实现


Sync(Callable<V> callable) {
    //这里的callable就是mWorker
    this.callable = callable;
}

只看Sync构造方法即可,可得知只是做了简单的保存。这条线索“FutureTask类的声明和实现”也挖到底了,


【AsyncTask机制逻辑流程图 v4】:
这里写图片描述


回到mFuture对象初始化时(在Asynctask构造方法中),它在new 了一个FutureTask类中,覆盖重写了父类的done方法,先暂缓done方法具体实现的分析。

我们之前说过“执行mFuture对象就是在执行run方法”!那么现在查找run方法在何处被调用

//FutureTask类中的方法
//mFuture的run方法被调用了
public void run() {
    sync.innerRun();
}

run方法中又调用Sync对象innerRun方法,深入,查看其具体实现:

void innerRun() {
    ...
        //调用mWorker的call()
        result = callable.call();
        ...
        set(result);
    ...
}

innerRun方法中,调用了callable(也就是mWorker)的call方法,因为在syncnew出来的时候,在构造方法中就已经把mWorker赋值给了callable,所以实际上是调用mWorker的call方法所以一大悬案—–问题二“为何doInBackground方法运行在子线程”已被侦破:

mFuturerun方法肯定执行在子线程中,run方法又调用innerRun方法,其也在子线程中,innerRun方法又调用了mWorkercall方法!其call方法中又调用了 doInBackground方法,所以它必然执行在子线程(这一系列的推理一定要掌握)


【AsyncTask机制逻辑流程图 v5】:
这里写图片描述


理解之后,新的问题又来了,doInBackground方法如何将其返回值传给onPostExecute方法中,它是如何被回调的?怎么又在主线程中运行的?莫方!回到上面代码继续往下:
result = callable.call();
调用mWorkercall方法后有一个返回值result
set(result);
又调用了set方法(方法在Sync对象的innerRun方法里面),将其保存起来。查看set方法具体实现

//FutureTask类中的方法
protected void set(V v) {
     sync.innerSet(v);
}

set方法(在FutureTask类中)中又调用Sync对象的innerSet方法,继续深入查看:

void innerSet(V v){
    ...
    ...
     if (compareAndSetState(s, RAN)) {
                result = v;
                releaseShared(0);
                //关键的done方法
                done();
                return;
      }
  }

innerSet方法中调用了done方法,怎么有点眼熟?!查看其实现:

//FutureTask类中的方法
protected void done(){}

done方法(在FutureTask类中)没有方法体,那调用此方法有何意义?但存在即合理!这个方法肯定是在new 一个FutureTask类时,将其覆盖重写了!这条线索“执行mFuture对象就是在执行run方法”已完毕,带着我们的猜测回到mFuture初始化AsyncTask构造方法)代码中,上面有代码,再次粘贴主要部分:

mFuture = new FutureTask<Result>(mWorker){
    //此处重写done方法
    @Override
    protected void done() {
        ...
        //获取doInbackground方法返回的结果
        result = get();
        ...
        //创建一个消息
        message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(AsyncTask.this, result));
        //把这条消息发送给创建这个消息的Handler:target.sendMessage(this)
        message.sendToTarget();
    }
    };

首先是get方法,之前在FutureTask中,Sync变量保存了doInBackground返回值!所以这里取出结果,取出来后创建了一个消息,传入了两个参数:MESSAGE_POST_RESULT(是post的一个结果)、new了一个AsyncTaskresult类,并将doInBackground返回值传入到newAsyncTaskresult的参数中。


【AsyncTask机制逻辑流程图 v6】:
这里写图片描述


其实创建消息时接受的两个参数,一个是post结果,一个是Message里面的Object,用Object保存了一个AsyncTaskresult对象,这个对象就保存有返回值。最后调用messagesendToTarget方法,这个方法感觉很陌生,继续深入,查找其实现:

public void sendToTarget(){
    target.sendMessage(this);
}

target调用了sendMessage方法,其实这个target就是一个hanlder,来发送消息,由于sendToTarget方法在Message内部,所以传入参数this,就是自身。一发消息就会发到Handler中,查找Handler何处声明

private static final InternalHandler sHandler =  new InternalHandler();

Handler在这里new 出来了,是一个InternalHandler 类,里面一定有一个hanlderMessage方法来处理接收的消息:

private static class InternalHandler extends Handler{
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                //调用finish方法
                result.mTask.finish(result.mData[0]);
                break;
             ...
             ...
        }
    }
}

果然如此,声明了一个内部类继承于Handler重写了父类的handleMessage方法,所以之前mFuture对象重写的done**方法**中发送的消息会发送至此!

之前创建消息的时候将doInBackground返回值作为参数传入到AsyncTaskresult类中(也就是MessageObject类),现在将result取出来,执行了:

result.mTask.finish(result.mData[0]);

这个mTask就是之前创建消息时传入的第一个参数,也就是AsyncTask本身mData[0]就是doInBackground返回值。再看AsyncTask对象的finish的方法体:

private void finish(Result result) {
 if (isCancelled()) result = null;
    //调用onPostExecute方法,并传入结果
    onPostExecute(result);
    mStatus = Status.FINISHED;
}

首先看方法体中的参数result就是doInBackground返回值,注意注意!此处就回调了onPostExecute方法,并传入结果所以又一大悬案“onPostExecute如何在doInBackground执行完后实行回调并得到结果?”已侦破!

可是最后一大悬案“onPostExecute方法为何运行在主线程?”

是因为通过Handler发送消息实现的!通过InternalHandler发消息,一发就跑到主线程了呀!






二. AsyncTask机制逻辑流程图【最终版】

这里写图片描述






三. AsyncTask机制归纳

AsyncTask 异步任务, 主要包含两个关键类:

(1)InternalHandler : 消息处理器, 用于处理线程之间消息.

(2)ThreadPoolExecutor : 线程池, 用于处理耗时任务.



归纳:

结合源代码分析之后,我们会发现主要是InternalHandlerThreadPoolExecutor这两个类起着重要作用!

所以,Android 原生的 AsyncTask机制 就是对线程池(也就是ThreadPoolExecutor)的一个封装,使用其自定义Executor来调度线程的执行方式并发还是串行),最后使用 Handler(也就是InternalHandler) 来完成子线程主线程数据共享






其实写此博客之前,我搜到网上已经有很多大牛分析过了,并且更加精髓。我在考虑自己再写会不会有点炒冷饭的感觉,可是别人的东西毕竟是别人的,多总结归纳总是有好处的,共勉~

最后!我讨厌csdn博客保存功能,因为之前没保存,重写了两次,心碎!但是当我重写第二遍的时候,有些细节部分更明白了,所谓温故而知新啊,也许第一遍看源代码分析不太懂,但是多看几遍就理解了!而且此篇博客关键在于一步步分析,十分详细!有何问题欢迎讨论。

希望对你们有帮助:)

作者:ITermeng 发表于2016/9/29 18:52:24 原文链接
阅读:212 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles