从源码的角度了解AsyncTask
可能最近时间比较紧张,没有足够的时间去学习新东西,那就利用这紧张的时间再来复习AsyncTask吧。
首先我们应该清楚AsyncTask主要用来实现什么业务,有什么优点,为什么要用它对吧。
诉求:
1. 我们知道,在Android中,如果执行耗时操作,一定不能在主线程中执行,而且更新UI界面又必须在在主线程中操作。
2. 如果耗时操作比较多,很多人一般情况下就会开启很多子线程去执行耗时任务,所以这样一来,这就给系统带来非常大的负担,随之而来就影响了整个系统和整个App的性能。
3. 有时候我们在很多业务场景中,需要启动很多线程,那么线程与线程之间的交互,线程之间的通信。
所以针对上面三条诉求(严格来说还有其他的一些诉求),我们起初会更愿意使用AsyncTask来实现满足这样的诉求,所以AsyncTask不仅可以有效管理线程数量,任务排序调度,还能实现线程之间的通信,还有有效解决android中的ANR,以及UI界面的更新问题,AsyncTask做了这一些列的封装。
public abstract class AsyncTask<Params, Progress, Result> {....}
所以很明显,AsyncTask是一个抽象类,必须继承派生出来一个子类才能使用。
AsyncTask定一个了三个比较有意思的泛型参数:
Params:启动Task的时候需要传入,也就是说调用AsyncTask对象的execute(Params)传入。
@Override
protected String doInBackground(Integer... integers) {
return null;
}
Progress: 后台执行的一个百分比值(非严格来说就是这样的)。
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);//可以更新一些进度信息或者进度条
}
但是这里需要注意的是,onProgressUpdate(Progress)方法是通过AsyncTask内部一个final修饰的方法publishProgress(Progress)调用的。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
最后通过发送一个Handler消息MESSAGE_POST_PROGRESS,给Handler,我们再看这个Handler如果合理这个消息的(我们先看流程,不要忽略Handler是子线程的还是主线程的,但是能更新进度界面,那肯定是主线程咯)。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
所以大家看到了吧,最后调用了onProgressUpdate方法,然后,所以我们派生继承子类的时候,把这个方法覆盖实现就能处理我们自己业务逻辑上的进度更新,因为publishProgress是一个final类型的,无法被Override。
Result:这是后台任务执行完毕后,输出给UI界面的数据,最后后台任务执行完毕,会通过调用onPostExecute(Result)输出给UI线程,所以这个方法执行是在主线程的。
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
OK,上麦年我们分别简单说了一下三个反泛型参数的意思。
那么接下来我们再说说几个功能性的成员方法吧。
//在执行后台任务之前可以在这个方法中做一些事情(主线程)
@MainThread
protected void onPreExecute() {
}
//后台任务都放在这个方法中,在后台子线程中执行
@WorkerThread
protected abstract Result doInBackground(Params... params);
//后台任务执行进度更新的逻辑放在这个方法中处理,在主线程中执行
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//后台任务执行结束,执行结果通过参数传递过来,执行结束的逻辑在这处理,
//在主线程中执行
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
//取消结束一个任务,调用方法之后,会在之后调用onCancelled(Result)方法
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
//当用户调用取消之后回调,在主线程中执行
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
onCancelled();
}
OK,通过在代码中加注释分别介绍了几个方法的功能和使用场景,相信大家都应该能理解。
那既然也介绍了那么多,那就顺带也来写个小Demo,模拟一下下载任务吧。
package com.zhg.views.asynctask.demo;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private DonwnloadAsyncTask mDownloadAsyncTask ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDownloadAsyncTask = new DonwnloadAsyncTask();
}
@Override
protected void onResume() {
super.onResume();
//启动任务,传入一个数字100的int类型参数
mDownloadAsyncTask.execute(100);
}
@Override
protected void onPause() {
super.onPause();
//当Activity进入onStop的时候调用cancel
mDownloadAsyncTask.cancel(true);
}
//派生继承AsyncTask
private class DonwnloadAsyncTask extends AsyncTask<Integer,Integer,String>{
@Override
protected String doInBackground(Integer... integers) {
if(integers!=null && integers[0] !=null) {
for(int i = 0 ;i < integers[0] ; i++){
//把进度通过publishProgress方法提交到主线程中的onProgressUpdate
publishProgress(i+1);
try {
Thread.sleep(1000);//模拟下载的延时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return "执行完毕";//后台任务处理完毕,返回处理结果
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if(values!=null && values[0]!=null){
//在这里更新进度
mTextView.setText(values[0]+"");
}
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
mTextView.setText(s);//显示处理结果
}
@Override
protected void onPreExecute() {
//这个就是在任务启动先获取TextView
super.onPreExecute();
mTextView = (TextView) findViewById(R.id.textview);
}
}
}
Demo比较简单,其实主要是想让大家了解这个AsyncTask的用法。
OK,现在我们就可以开始跟源码来了解原理了。
OK,那就从execute(Params)开始跟代码。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
代码很简单,就一行代码,但是有一个很重要的细节,在调用executeOnExecutor(sDefaultExecutor, params),的时候,传递了一个sDefaultExecutor参数,这个参数是啥呢?来看看先。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
SERIAL_EXECUTOR 是一个Executor ,我们知道Executor 是一个线程执行者,SerialExecutor内部类继承了Executor。
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//mTasks.offer向队尾插入元素
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();//一个任务执行之后,立即安排下一个任务执行
}
}
});
//最开始mActive 为null,立即安排一个任务执行
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
//把一个任务交给线程池执行
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
mTasks 很明显,是一个集合,是一个装着任务体的集合,Runnable。
上面源码的注释我都说清楚了,所以这sDefaultExecutor (SerialExecutor) 其实就一个用来安排一个线程到线程池中去执行,以及存储后台任务的角色。但是这里有一个比较重要的东西,那就是
THREAD_POOL_EXECUTOR,那么我们来看看THREAD_POOL_EXECUTOR这个玩意是什么吧。
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
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;
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);
public static final Executor THREAD_POOL_EXECUTOR;
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;
}
没错,THREAD_POOL_EXECUTOR就是一个活生生的线程池,创建了一个
大小为CORE_POOL_SIZE的线程池,这个的大小是多大?默认它是通过获取当前CPU的核数,根据CPU的核数来计算的,也就是说,当CPU核数小于4的时候,线程池为2,当CPU核数大于4个的时候,线程池的默认大小为4。
写个伪代码让大家理解理解:
int size = 2;
int cpuCount = ?;
if(cpuCount < 4){
size = 2;
}else if(cpuCount > 4){
size = 4;
}
MAXIMUM_POOL_SIZE 这个就是线程池最大的大小,是CPU核数的2倍加1。
然后在创建了一个任务队列sPoolWorkQueue,这个任务队列最大可以存放128个任务,大家注意,这个128不是线程池的大小,他只是表明这个队列可以存放128个任务而已,这个队列是一个先进先出的队列。
OK,到这里我们也就明白了,然后我们继续恢复到主线上来跟源码吧。
@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;
}
这个方法也很简单,先把状态标记为正在执行,然后调用onPreExeceute()方法,这个方法我们很熟悉,这个方法就是在主线程中执行的,用来在任务执行之前执行一些业务,然后把当前参数设置给了mWorker.mParams。
我们看看这个mWorker是个什么东西。
private final WorkerRunnable<Params, Result> mWorker;
....
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
我们发现WorkerRunnable是一个抽象类,而且这个抽象类同时还实现Callable接口,定义了一个成员变量Params[] mParams,没错,刚刚我们传进来的参数就是赋值给他了。
先看看Callable是个什么接口。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
很简单,就一个call方法,返回result。
接下来我们看看mWork是在哪里赋值创建的呢?
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
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 occurred while executing doInBackground()",e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
结果我们发现mWorker 是以匿名内部类的方式创建的,然后细心的人可能已经注意到,在call方法中调用了我们特别重要的一个方doInBackground(params)方法,OK,我们先不管,我们继续会到主线跟代码。
然后就是调用exec的.execute(mFuture)方法,在一开始我们就介绍了AsyncTask默认的sDefaultExecutor ,这个就是用来存储任务,和安排一个任务到线程池去执行的,他不是直接的去执行任务,他最终他会交给线程池去调度一个空闲线程去执行,我们知道把线程池的execute方法,都接受一个Runnable对象,那么这里的mFuture其实就是一个Runnable对象,稍微看看吧。
private final FutureTask<Result> mFuture;
这个我们知道,在上面Async的Task方法中就已经实例化了这个对象,也是通过匿名内部类的方式创建的,Override了一个done方法,需要注意的是,这创建的时候,在它的构造方法中传递了mWorker对象进去了。
从这样来看,也没发现它是一个Runnable,我们继续刨根问题,再看FutureTask是个啥东西。
public class FutureTask<V> implements RunnableFuture<V> {
...
public void run() {
if (state != NEW ||!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
}
到这里大家应该一目了然了吧,这个还真是一个Runnable对象,并且已经实现了run()方法,而且这个run方法里面还拿到我们传递给他的mWorker对象,即Callable c = callable,然后直接调用mWorker的call方法,我们前面分析过,mWorker是通过创建匿名内部WorkerRunnable的方式创建的,他实现了Callable接口,Callable有一个call方法,而我们也发现,在mWorker的call方法中调用doInBackground(…)方法,就这样,我们的后台任务业务代码,就这样在子线程中得以执行了。
OK,我们继续回到主线,我们把任务提交给了线程池执行之后,如果任务执行完了,怎么回调我们的onPostExecute(Result)呢?
我们这时候应该回到WorkerRunnable的call方法中去,发现doInBackground方法后面还有一行特别关键的代码,即
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
....
}
对,没错,就是postResult(result);很明显,从字面理解就是,提交处理结果。我们去看看这个方法怎么处理的,怎么把结果抛给主线程方法onPostExecute()方法中去的。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
.....
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
现在我们的代码比较熟悉了把,直接创建了一个Message,我们都知道Mesage一般都是和Handler结合使用的。
终于我发现下一行代码就直接调用了getHandler()获取一个Handler对象来产生了一个Message对象,我们知道用Handler的obtainMessage方法缠身的Message对象,起内部的target默认就是当前这个Handler。
所以最后我们把处理结果进行了封装,封装成AsyncTaskResult,AsyncTaskResult中的mTask接收的就是构造方法中传递的this,而mData就是构造方法中传递过来的result,最后把消息发送了出去,那么无可厚非,我么那直接看Handler怎么创建和怎么处理消息的把。
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
代码很简单,创建了一个静态的方法,静态方法中创建了一个InternalHandler对象,那么这个对象很明显就是一个Handler。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
所以到这里大家都很清楚了,Handler创建的时候,构造方法中传递的Looper是MainLooper,即主线程的Looper,所以onProgressUpdate和onPostExecute方法最终是在主线程执行的 , 最后调用了result.mTask.finish(result.mData[0]);
result 我们知道是AsyncTaskResult对象,mTask其实就是AsyncTask自己,最后调用了AsyncTask的finish(…)我们来看看把。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
很简单最后,我就不说了,大家都明白。其实呢,到这里基本讲完了。
最后说说AsyncTask平时使用的一些细节。
我们知道,最新的AsyncTask,在很多方面都已经得到改进,比如,线程池的问题,我们就已经发现很多好处,不知道大家通过上面的分析, 有没有注意到,其实executeOnExecutor是一个public的方法,这个方法第一个参数接受一个Executor 的参数,这是一个非常好的有点,一个扩展点,如果那天AsyncTask内部默认维护的线程池我们满足不了我么你的需求,我们可以通过外界传递一个自定义的线程池。
另外还有注意几点:
1.AsyncTask的实例必须在UI thread中创建,并且execute方法必须在UI thread中调用,但其实我个人认为这也是一个缺点,如果我不在UI线程中创建,那onPreExecute()方法执行就是在子线程中了,但是往往onPreExecute方法很多业务逻辑都在UI线程。
- 不要自己手动的调用
onPreExecute(),
onPostExecute(Result),
doInBackground
onProgressUpdate(Progress…)这几个方法 - 该task只能被执行一次,否则多次调用时将会出现异常。