转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17596225
什么是asynctask,相信搞过android开发的朋友们都不陌生。asynctask内部封装了thread和handler,可以让我们在后台进行计算并且把计算的结果及时更新到ui上,而这些正是thread+handler所做的事情,没错,asynctask的作用就是简化thread+handler,让我们能够通过更少的代码来完成一样的功能,这里,我要说明的是:asynctask只是简化thread+handler而不是替代,实际上它也替代不了。同时,asynctask从最开始到现在已经经过了几次代码修改,任务的执行逻辑慢慢地发生了改变,并不是大家所想象的那样:asynctask是完全并行执行的就像多个线程一样,其实不是的,所以用asynctask的时候还是要注意,下面会一一说明。另外本文主要是分析asynctask的源代码以及使用时候的一些注意事项,如果你还不熟悉asynctask,请先阅读android之asynctask 来了解其基本用法。
这里先给出asynctask的一个例子:
[java] view
plaincopy
private class downloadfilestask extends asynctask<url, integer, long> {
protected long doinbackground(url... urls) {
int count = urls.length;
long totalsize = 0;
for (int i = 0; i < count; i++) {
totalsize += downloader.downloadfile(urls[i]);
publishprogress((int) ((i / (float) count) * 100));
// escape early if cancel() is called
if (iscancelled()) break;
}
return totalsize;
}
protected void onprogressupdate(integer... progress) {
setprogresspercent(progress[0]);
protected void onpostexecute(long result) {
showdialog("downloaded " + result + " bytes");
}
asynctask的类必须在ui线程加载(从4.1开始系统会帮我们自动完成)
asynctask对象必须在ui线程创建
execute方法必须在ui线程调用
不要在你的程序中去直接调用onpreexecute(), onpostexecute, doinbackground, onprogressupdate方法
一个asynctask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
asynctask不是被设计为处理耗时操作的,耗时上限为几秒钟,如果要做长耗时操作,强烈建议你使用executor,threadpoolexecutor以及futuretask
在1.6之前,asynctask是串行执行任务的,1.6的时候asynctask开始采用线程池里处理并行任务,但是从3.0开始,为了避免asynctask所带来的并发错误,asynctask又采用一个线程来串行执行任务
给大家做一下实验,请看如下实验代码:代码很简单,就是点击按钮的时候同时执行5个asynctask,每个asynctask休眠3s,同时把每个asynctask执行结束的时间打印出来,这样我们就能观察出到底是串行执行还是并行执行。
@override
public void onclick(view v) {
if (v == mbutton) {
new myasynctask("asynctask#1").execute("");
new myasynctask("asynctask#2").execute("");
new myasynctask("asynctask#3").execute("");
new myasynctask("asynctask#4").execute("");
new myasynctask("asynctask#5").execute("");
}
}
private static class myasynctask extends asynctask<string, integer, string> {
private string mname = "asynctask";
public myasynctask(string name) {
super();
mname = name;
@override
protected string doinbackground(string... params) {
try {
thread.sleep(3000);
} catch (interruptedexception e) {
e.printstacktrace();
}
return mname;
protected void onpostexecute(string result) {
super.onpostexecute(result);
simpledateformat df = new simpledateformat("yyyy-mm-dd hh:mm:ss");
log.e(tag, result + "execute finish at " + df.format(new date()));
我找了2个手机,系统分别是4.1.1和2.3.3,按照我前面的描述,asynctask在4.1.1应该是串行的,在2.3.3应该是并行的,到底是不是这样呢?请看log
android 4.1.1上执行:从下面log可以看出,5个asynctask共耗时15s且时间间隔为3s,很显然是串行执行的
android 2.3.3上执行:从下面log可以看出,5个asynctask的结束时间是一样的,很显然是并行执行
结论:从上面的两个log可以看出,我前面的描述是完全正确的。下面请看源码,让我们去了解下其中的原理。
/*
* copyright (c) 2008 the android open source project
*
* licensed under the apache license, version 2.0 (the "license");
* you may not use this file except in compliance with the license.
* you may obtain a copy of the license at
* http://www.apache.org/licenses/license-2.0
* unless required by applicable law or agreed to in writing, software
* distributed under the license is distributed on an "as is" basis,
* without warranties or conditions of any kind, either express or implied.
* see the license for the specific language governing permissions and
* limitations under the license.
*/
package android.os;
import java.util.arraydeque;
import java.util.concurrent.blockingqueue;
import java.util.concurrent.callable;
import java.util.concurrent.cancellationexception;
import java.util.concurrent.executor;
import java.util.concurrent.executionexception;
import java.util.concurrent.futuretask;
import java.util.concurrent.linkedblockingqueue;
import java.util.concurrent.threadfactory;
import java.util.concurrent.threadpoolexecutor;
import java.util.concurrent.timeunit;
import java.util.concurrent.timeoutexception;
import java.util.concurrent.atomic.atomicboolean;
import java.util.concurrent.atomic.atomicinteger;
public abstract class asynctask<params, progress, result> {
private static final string log_tag = "asynctask";
//获取当前的cpu核心数
private static final int cpu_count = runtime.getruntime().availableprocessors();
//线程池核心容量
private static final int core_pool_size = cpu_count + 1;
//线程池最大容量
private static final int maximum_pool_size = cpu_count * 2 + 1;
//过剩的空闲线程的存活时间
private static final int keep_alive = 1;
//threadfactory 线程工厂,通过工厂方法newthread来获取新线程
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());
};
//静态阻塞式队列,用来存放待执行的任务,初始容量:128个
private static final blockingqueue<runnable> spoolworkqueue =
new linkedblockingqueue<runnable>(128);
/**
* 静态并发线程池,可以用来并行执行任务,尽管从3.0开始,asynctask默认是串行执行任务
* 但是我们仍然能构造出并行的asynctask
*/
public static final executor thread_pool_executor
= new threadpoolexecutor(core_pool_size, maximum_pool_size, keep_alive,
timeunit.seconds, spoolworkqueue, sthreadfactory);
* 静态串行任务执行器,其内部实现了串行控制,
* 循环的取出一个个任务交给上述的并发线程池去执行
public static final executor serial_executor = new serialexecutor();
//消息类型:发送结果
private static final int message_post_result = 0x1;
//消息类型:更新进度
private static final int message_post_progress = 0x2;
/**静态handler,用来发送上述两种通知,采用ui线程的looper来处理消息
* 这就是为什么asynctask必须在ui线程调用,因为子线程
* 默认没有looper无法创建下面的handler,程序会直接crash
private static final internalhandler shandler = new internalhandler();
//默认任务执行器,被赋值为串行任务执行器,就是它,asynctask变成串行的了
private static volatile executor sdefaultexecutor = serial_executor;
//如下两个变量我们先不要深究,不影响我们对整体逻辑的理解
private final workerrunnable<params, result> mworker;
private final futuretask<result> mfuture;
//任务的状态 默认为挂起,即等待执行,其类型标识为易变的(volatile)
private volatile status mstatus = status.pending;
//原子布尔型,支持高并发访问,标识任务是否被取消
private final atomicboolean mcancelled = new atomicboolean();
//原子布尔型,支持高并发访问,标识任务是否被执行过
private final atomicboolean mtaskinvoked = new atomicboolean();
/*串行执行器的实现,我们要好好看看,它是怎么把并行转为串行的
*目前我们需要知道,asynctask.execute(params ...)实际上会调用
*serialexecutor的execute方法,这一点后面再说明。也就是说:当你的asynctask执行的时候,
*首先你的task会被加入到任务队列,然后排队,一个个执行
private static class serialexecutor implements executor {
//线性双向队列,用来存储所有的asynctask任务
final arraydeque<runnable> mtasks = new arraydeque<runnable>();
//当前正在执行的asynctask任务
runnable mactive;
public synchronized void execute(final runnable r) {
//将新的asynctask任务加入到双向队列中
mtasks.offer(new runnable() {
public void run() {
try {
//执行asynctask任务
r.run();
} finally {
//当前asynctask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话
//这一点很明显体现了asynctask是串行执行任务的,总是一个任务执行完毕才会执行下一个任务
schedulenext();
}
}
});
//如果当前没有任务在执行,直接进入执行逻辑
if (mactive == null) {
schedulenext();
}
protected synchronized void schedulenext() {
//从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行
if ((mactive = mtasks.poll()) != null) {
thread_pool_executor.execute(mactive);
* 任务的三种状态
public enum status {
/**
* 任务等待执行
*/
pending,
* 任务正在执行
running,
* 任务已经执行结束
finished,
/** 隐藏api:在ui线程中调用,用来初始化handler */
public static void init() {
shandler.getlooper();
/** 隐藏api:为asynctask设置默认执行器 */
public static void setdefaultexecutor(executor exec) {
sdefaultexecutor = exec;
* 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 {
mtaskinvoked.set(true);
process.setthreadpriority(process.thread_priority_background);
//noinspection unchecked
return postresult(doinbackground(mparams));
};
mfuture = new futuretask<result>(mworker) {
@override
protected void done() {
try {
postresultifnotinvoked(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) {
postresultifnotinvoked(null);
private void postresultifnotinvoked(result result) {
final boolean wastaskinvoked = mtaskinvoked.get();
if (!wastaskinvoked) {
postresult(result);
//doinbackground执行完毕,发送消息
private result postresult(result result) {
@suppresswarnings("unchecked")
message message = shandler.obtainmessage(message_post_result,
new asynctaskresult<result>(this, result));
message.sendtotarget();
return result;
* 返回任务的状态
public final status getstatus() {
return mstatus;
* 这个方法是我们必须要重写的,用来做后台计算
* 所在线程:后台线程
protected abstract result doinbackground(params... params);
* 在doinbackground之前调用,用来做初始化工作
* 所在线程:ui线程
protected void onpreexecute() {
* 在doinbackground之后调用,用来接受后台计算结果更新ui
protected void onpostexecute(result result) {
* runs on the ui thread after {@link #publishprogress} is invoked.
/**
* 在publishprogress之后调用,用来更新计算进度
protected void onprogressupdate(progress... values) {
* cancel被调用并且doinbackground执行结束,会调用oncancelled,表示任务被取消
* 这个时候onpostexecute不会再被调用,二者是互斥的,分别表示任务取消和任务执行完成
@suppresswarnings({"unusedparameters"})
protected void oncancelled(result result) {
oncancelled();
}
protected void oncancelled() {
public final boolean iscancelled() {
return mcancelled.get();
public final boolean cancel(boolean mayinterruptifrunning) {
mcancelled.set(true);
return mfuture.cancel(mayinterruptifrunning);
public final result get() throws interruptedexception, executionexception {
return mfuture.get();
public final result get(long timeout, timeunit unit) throws interruptedexception,
executionexception, timeoutexception {
return mfuture.get(timeout, unit);
* 这个方法如何执行和系统版本有关,在asynctask的使用规则里已经说明,如果你真的想使用并行asynctask,
* 也是可以的,只要稍作修改
* 必须在ui线程调用此方法
public final asynctask<params, progress, result> execute(params... params) {
//串行执行
return executeonexecutor(sdefaultexecutor, params);
//如果我们想并行执行,这样改就行了,当然这个方法我们没法改
//return executeonexecutor(thread_pool_executor, params);
* 通过这个方法我们可以自定义asynctask的执行方式,串行or并行,甚至可以采用自己的executor
* 为了实现并行,我们可以在外部这么用asynctask:
* asynctask.executeonexecutor(asynctask.thread_pool_executor, params... params);
public final asynctask<params, progress, result> executeonexecutor(executor exec,
params... params) {
if (mstatus != status.pending) {
switch (mstatus) {
case running:
throw new illegalstateexception("cannot execute task:"
+ " the task is already running.");
case finished:
+ " the task has already been executed "
+ "(a task can be executed only once)");
mstatus = status.running;
//这里#onpreexecute会最先执行
onpreexecute();
mworker.mparams = params;
//然后后台计算#doinbackground才真正开始
exec.execute(mfuture);
//接着会有#onprogressupdate被调用,最后是#onpostexecute
return this;
* 这是asynctask提供的一个静态方法,方便我们直接执行一个runnable
public static void execute(runnable runnable) {
sdefaultexecutor.execute(runnable);
* 打印后台计算进度,onprogressupdate会被调用
protected final void publishprogress(progress... values) {
if (!iscancelled()) {
shandler.obtainmessage(message_post_progress,
new asynctaskresult<progress>(this, values)).sendtotarget();
//任务结束的时候会进行判断,如果任务没有被取消,则onpostexecute会被调用
private void finish(result result) {
if (iscancelled()) {
oncancelled(result);
} else {
onpostexecute(result);
mstatus = status.finished;
//asynctask内部handler,用来发送后台计算进度更新消息和计算完成消息
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);
private static abstract class workerrunnable<params, result> implements callable<result> {
params[] mparams;
@suppresswarnings({"rawuseofparameterizedtype"})
private static class asynctaskresult<data> {
final asynctask mtask;
final data[] mdata;
asynctaskresult(asynctask task, data... data) {
mtask = task;
mdata = data;
通过上面的源码分析,我已经给出了在3.0以上系统中让asynctask并行执行的方法,现在,让我们来试一试,代码还是之前采用的测试代码,我们要稍作修改,调用asynctask的executeonexecutor方法而不是execute,请看:
@targetapi(build.version_codes.honeycomb)
new myasynctask("asynctask#1").executeonexecutor(asynctask.thread_pool_executor,"");
new myasynctask("asynctask#2").executeonexecutor(asynctask.thread_pool_executor,"");
new myasynctask("asynctask#3").executeonexecutor(asynctask.thread_pool_executor,"");
new myasynctask("asynctask#4").executeonexecutor(asynctask.thread_pool_executor,"");
new myasynctask("asynctask#5").executeonexecutor(asynctask.thread_pool_executor,"");
下面是系统为4.1.1手机打印出的log:很显然,我们的目的达到了,成功的让asynctask在4.1.1的手机上并行起来了,很高兴吧!希望这篇文章对你有用。