Android AsyncTask 源码解析

是什么东西?

顾名思义,异步任务,就是说我们可以让我们异步执行任务,不过通常使用它是为了异步执行,主线程更新UI,我们都知道,Android 的UI更新操作,都会检查是否是主线程,如果不是的话就会报出异常,这一步是在ViewRootImpl里面做的,这里就不多说了。在使用的时候,我们只需要将后台处理的代码写在doInBackground,更新UI的工作写在onPostExecute就行了,就可以轻松实现一个线程切换,更新UI的效果。那这里你可能会问。为什么不用Handler,我也许会告诉你,使用 AsyncTask 有很多好处,待会会慢慢告诉你,你也可以根据源码,一探究竟。

前世今生

没有继承自任何东西,Object 除外,使用了模板定义,Params 代表你要传的参数类型,Progress 代表更新数据类型,Result 是任务执行玩返回数据类型。

1
public abstract class AsyncTask<Params, Progress, Result> {}

使用场景

只要需要开启子线程的都可以使用它。在项目中,随便 new Thread 并不是一个好习惯,滥用不说,很有可能造成内存泄露

实现原理大白话

有两个东西很关键,一个是线程池,一个是 Handler,线程池负责执行任务, Handler 负责通知,说道这,聪明的你大概猜到了实现方式,但这并不够,我们需要细细的品味这个类是如何写成的。

源码解析

属性

通过使用 Runtime 对象获取可用的cpu 个数,在创建线程池时创建合适的线程数量,线程池的参数主要是核心线程数,这里最小值2, 最大值4,当处理器个数为4时,创建3个核心线程数,最大线程数为 cpu个数的两倍 + 1,关于核心线程数,最大线程数,大家可自行去了解作用,以后创建线程池,大家可要记住了,以这样创建线程池数是比较合理的。KeepAliveTime 是线程存活时间,超过这个时间的线程要被回收,至于哪一些线程,待会再说

1
2
3
4
5
6
7
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;

静态线程工厂,在类加载是就会被创建,里面使用了AtomicInteger 用来计数。使用了LinkedBlockingQueue作为任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);

/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;

使用静态代码块初始化线程池,参数为以上分析的数据,注意,allowCoreThreadTimeOut 设置为true,意思是允许核心线程数被回收,默认是不会被回收的

1
2
3
4
5
6
7
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

还有一个重头戏,单任务执行器,默认不是用上面的线程池执行任务,而是使用序列执行器执行任务,意识就是,同一时刻只能执行一个任务,注意这些都是静态的,也就是说,一个进程,不管你新建了多少个 Task对象,他也只能单个任务执行,这样就很好的管理了资源,那他是如何做到单任务执行的呢,实现方式可以说是既巧妙又无语,且看注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
// 保存任务
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
// 新任务进来,不执行,而是使用匿名内部类创建一个Task加到任务队列里,
// 当这个执行完的时候,才会去调用下一个任务,就做到了单任务执行。
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
// 默认先
if (mActive == null) {
scheduleNext();
}
}

// 这里是真正去执行的
// 注意,使用到了 synchronized 的可重入特性!
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}

一些静态的变量基本看完了,下面来看一下构造函数,有点长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
// 首先获取 Handler 对象,这里有主线程 Handler 和子线程Handler 之分
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);

// 异步执行的,实现了callable接口
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
// 为了防止多次多次调用实例
mTaskInvoked.set(true);
Result result = null;
try {
// 设置进程的优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// noinspection unchecked
// 在这里执行doInBackground函数,
// 这是我们需要重写的
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 执行完,这里还是在子线程
postResult(result);
}
return result;
}
};

// FutureTask 对象,封装Callable 对象
mFuture = new FutureTask<Result>(mWorker) {
// 结束时调用
@Override
protected void done() {
try {
// 如果没有被调用的话,通过get获取结果
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}

执行完以后,使用Handler 进行通知,注意,这里将结果装入Message对象中,直接发送。一个小技巧,我们在发送消息是尽可能使用 obtainMessage 获取Message对象要比new一个好得多。

1
2
3
4
5
6
7
8
// 返回结果,使用handler
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}

那执行入口呢? MainThread 代表主线程执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
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:
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;
// 执行
exec.execute(mFuture);

return this;
}

一些我们可能需要实现的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//   我们将后台逻辑写在这里
@WorkerThread
protected abstract Result doInBackground(Params... params);

@MainThread
protected void onPreExecute() {
}
// 执行完回调
@MainThread
protected void onPostExecute(Result result) {
}
// 如果有进度条更新
@MainThread
protected void onProgressUpdate(Progress... values) {
}

@MainThread
protected void onCancelled() {
}

如何更新进度条?只需要在 doInBackground 中调用即可,将参数传入,使用handler机制

1
2
3
4
5
6
7
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}

Handler 处理消息的地方,静态类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}

@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;
}
}
}

当然,你也可以取消任务,不过只能取消还没执行的任务,底层是调用 thread.interrupt 方法,简单粗暴,关于他的使用,可以写一堆东西来说,这里暂且不展开说

1
2
3
4
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}

当然,如果不要于UI交互,你也可以使用这个执行后台逻辑,直接调用executeOnExecutor,使用静态的execute提交,这里是单任务的

1
2
3
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}

当然,也可以多任务一起执行,调用executeOnExecutor时指定内部的线程池即可AsyncTask.THREAD_POOL_EXECUTOR

总结

我们了解这AsyncTask这个类,大致知道了他是怎么实现了,如果要进一步了解的话,线程池,锁这些东西必不可少的~

如有错误,欢迎讨论