AsyncTask异步任务笔记总结

一、AsyncTask概述

AsyncTask,是android提供的轻量级的异步类,作为一名安卓开发者,大家对这个类肯定不会陌生。作为抽象泛型类,它提供了Params、Progress、Result三个泛型参数,如果task确实不需要传递具体的参数,那么都可以设置为Void。下面是它的四个核心方法,其中doInBackground不是在主线程执行的。

onPreExecute、doInBackground、onProgressUpdate、onPostResult。


网上关于AsyncTask讲解的博客非常多,个人认为郭霖老师的讲解是比较通俗易懂的。关于AsyncTask他的博客地址是AsyncTask-郭霖

  

二、AsyncTask原理分析

以下是我根据博客以及书本,自己总结的AsyncTask工作原理。


1.AsyncTask的构造函数中定义了mWorker和和mFuture,mWorker是一个Callable对象,mFuture是一个FutureTask对象,在初始化mFuture的时候将mWorker作为参数传入,这两个变量会暂时保存在内存中。

2.当我们开始任务,也就是调用execute方法后,该方法返回executeOnExecutor方法,并传入两个参数sDefaultExecutor和 params,sDefaultExecutor是一个Executor对象,该对象是由SerialExecutor实例化而来。

3.在executeOnExecutor方法中,我们将传入的参数类型params赋值给mWorker的变量mparams,然后调用sDefaultExecutor这个对象的execute方法,同时传入上文的mFuture对象作为参数。

4.在sDefaultExecutor所代表的SerialExecutor类中,可以看到他的execute方法就是开启一个线程并执行传入的mFuture对象的run方法,而在这个方法里又会去调用Sync内部类的innerRun()方法

5.在innerRun方法里,调用了Callable的call方法,这个Callable毫无疑问就是上文的mWorker,也就是说执mFuture的run方法间接调用了mWorker的call方法,在这个方法里它返回了postResult(doInBackground(mParams)),可见此时程序还是运行在我们在步骤四里面开启的线程里,这就是我们为什么可以在doInBackground(mParams)里处理耗时逻辑的原因。

6.在postResult方法中,我们将doInBackground(mParams)返回的结果作为参数传了进去,在他的源码里我们可以看到它启用了android的消息传递机制-handler,它使用sHandler发送出了一条消息,sHandler是一个静态的Handler对象,它是InternalHandler类的一个实例,用于将执行环境从线程池切换到主线程。为了将执行环境切换到主线程,sHandler这个对象必须在主线程中创建。由于静态成员变量在加载类的时候进行初始化,因此变相AsyncTask的类必须在主线程中加载。消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象。

7.在InternalHandler的handleMessage()方法中,我们通过对传入的AsyncTaskResult对象的类型进行判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。

8.接下来就是finish方法的分析,他的源码如下

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

可以看到,如果当前任务被取消掉了,就会调用onCancelled()方法,如果没有被取消,则调用onPostExecute()方法,这样当前任务的执行就全部结束了。
 

9.在步骤七里面,还有一种MESSAGE_POST_PROGRESS的消息类型,这种消息是用于当前进度的,调用的正是onProgressUpdate()方法,我们可以看到这一种消息类型正是在publishProgress()方法中发出的,正因如此,在doInBackground()方法中调用publishProgress()方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作。

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


基本的步骤就是这样了,下面是补充说明:


在上面九个步骤中,我们在第二个步骤提到了SerialExecutor,它是一个线程池,用于任务的排队,在第六个步骤中我们提到了InternalHandler,它是一个Handler,用于将执行环境从线程池切换到主线程,除此之外,AsyncTask还用到了一个线程池,ThreadPoolExecute,用于真正执行任务,它的调用是在SerialExecutor类中,源代码如下

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);  
        }  
    }  
}  
可以看到,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,如果我们一次性启动了很多个任务,第一次运行execute()方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后判断mActive对象是不是等于null,第一次运行肯定是null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。
之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。
那么后面添加的任务岂不是永远得不到处理了?当然不是,看一看offer()方法里传入的Runnable匿名类,这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。



在Android1.6之前,AsyncTask是串行执行任务的,1.6-2.32的时候开始采用线程池处理并行任务。但是从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务,SerialExecutor也正是3.0才出现的,它在AsyncTask中是以常量的形式被使用的,因此在整个应用程序中的所有AsyncTask实例都会共用同一个SerialExecutor。但是尽管如此,我们仍然可以用AsyncTask的executeOnExecutor方法来并发执行任务。


好了,以上就是我对AsyncTask的一点理解。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值