相信大家对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")
问题:
- onPreExecute() 方法为何在主线程中执行?为何最先执行?
- doInBackground()方法为何在子线程中执行?如何实现该方法执行完就回调 onPostExecute() 方法?
- 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就是 一开始调用AsyncTask的execute方法时传进来的参数,此参数被保存到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数组,来保存了一开始调用AsyncTask的execute方法时传进来的参数!
【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的构造方法中(即AsyncTask一new出来的时候这两个对象就都实例化了!)。并且mFuture在new 一个 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方法,因为在sync被new出来的时候,在构造方法中就已经把mWorker赋值给了callable,所以实际上是调用mWorker的call方法!所以一大悬案—–问题二“为何doInBackground方法运行在子线程”已被侦破:
mFuture的run方法肯定执行在子线程中,run方法又调用了innerRun方法,其也在子线程中,innerRun方法又调用了mWorker的call方法!其call方法中又调用了 doInBackground方法,所以它必然执行在子线程!(这一系列的推理一定要掌握)
【AsyncTask机制逻辑流程图 v5】:
理解之后,新的问题又来了,doInBackground方法如何将其返回值传给onPostExecute方法中,它是如何被回调的?怎么又在主线程中运行的?莫方!回到上面代码继续往下:
result = callable.call();
调用了mWorker的call方法后有一个返回值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的返回值传入到new 的AsyncTaskresult的参数中。
【AsyncTask机制逻辑流程图 v6】:
其实创建消息时接受的两个参数,一个是post结果,一个是Message里面的Object,用Object保存了一个AsyncTaskresult对象,这个对象就保存有返回值。最后调用了message 的 sendToTarget方法,这个方法感觉很陌生,继续深入,查找其实现:
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类中(也就是Message的Object类),现在将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 : 线程池, 用于处理耗时任务.
归纳:
结合源代码分析之后,我们会发现主要是InternalHandler和ThreadPoolExecutor这两个类起着重要作用!
所以,Android 原生的 AsyncTask机制 就是对线程池(也就是ThreadPoolExecutor)的一个封装,使用其自定义的 Executor来调度线程的执行方式(并发还是串行),最后使用 Handler(也就是InternalHandler) 来完成子线程和主线程的数据共享。
其实写此博客之前,我搜到网上已经有很多大牛分析过了,并且更加精髓。我在考虑自己再写会不会有点炒冷饭的感觉,可是别人的东西毕竟是别人的,多总结归纳总是有好处的,共勉~
最后!我讨厌csdn博客保存功能,因为之前没保存,重写了两次,心碎!但是当我重写第二遍的时候,有些细节部分更明白了,所谓温故而知新啊,也许第一遍看源代码分析不太懂,但是多看几遍就理解了!而且此篇博客关键在于一步步分析,十分详细!有何问题欢迎讨论。
希望对你们有帮助:)