Android 多线程之 AsyncTask使用源码分析
一、前言
这主要是一个讲解 Android
中多线程的系列,全部文章如下:
- Android 多线程之 Handler 基本使用
- Android 多线程之 Handler 源码分析
- Android 多线程之 HandlerThread源码分析
- Android 多线程之 AsyncTask使用源码分析
- Android 多线程之 IntentService使用源码分析
上篇文章对 HandlerThread
的源码进行了分析,相信大家对 HandlerThread
有了一定的认识。如果我们需要执行一个异步任务,并且执行完毕之后在主线程中更新 UI
的话,使用 HandlerThread
就不是很划算了。
但是如果采用 Handler + Thread
的写法来进行异步任务的话,要写很多代码,首先要在主线程中创建一个 Handler
对象,然后再创建一个子线程用来处理异步任务,然后在任务处理完的时候还要调用 主线程中的 Handler
对象去发送消息,主线程中的 Handler
对象接收到消息以后才能在主线程中更新 UI
,这样做是很麻烦的。
不过 Android
给我们提供了一个更好的 Handler + Thread
的封装类 AsyncTask
来帮助我们更好的处理异步任务,然后更新 UI
。
接下来就来看下 AsyncTask
这个帮助类。
二、初识 AsyncTask
2.1 API 文档定义
老规矩,我们看下在 API
文档里面对 AsyncTask
的定义:
大概的意思是说:(以下翻译自 API
文档)
AsyncTask
能够让我们更加容易和合适的使用UI
线程,这个类允许我们在后台执行一些操作并且发送结果到主线程,而不用写很多Thread
和Handler
的代码。
AsyncTask
被设计为一个关于Thread
和Handler
的帮助类,而不是提供一个通用的线程框架,AsyncTask
应该被用于短时间的操作,如果你需要让线程长时间的运行,那么非常推荐使用java.util.concurrent
包提供的多样的API
,比如Exexutor
、ThreadPoolExecutor
和FutureTask
来处理。
AsyncTask
定义在子线程中处理计算,然后再发送结果到UI
线程,主要提供了三个常用类型的参数:Params
、Progress
和result
,并且有四个执行步骤:onPreExecute
、doInBackground
、onProgressUpdate
、onPostExecute
所以我们可以知道 AsyncTask
可以解决主线程不能进行异步任务和子线程不能更新 UI
这个问题。
根据该类的定义
public abstract class AsyncTask<Params, Progress, Result> {}
可以看到 AsyncTask
是一个抽象类并且有三个泛型参数,
2.2 泛型参数
三个泛型参数分别是:
Params
是AsyncTask
执行任务所需要的参数类型Progress
是在后台运行过程中,任务执行任务进度的参数类型Result
是最终计算得到的结果的参数类型
需要注意的是:
如果AsyncTask
确定不需要传递具体参数,那么这三个泛型参数可以用Void
来代替。
2.3 必须实现的抽象方法
他提供的抽象方法是
protected abstract Result doInBackground(Params... params);
这个方法前面已经说了: doInBackground
方法是运行在子线程中处理异步任务的,我们使用 AsyncTask
本身就是为了在子线程中处理异步任务,所以必须重写该方法。
2.4 其他的重要的可重写的方法
另外,还有其他的一些重要的重写方法如下
onPreExecute
(任务执行之前调用该方法,运行在 UI 线程)doInBackground
(任务执行的时候调用该方法,运行在子线程,主要进行耗时操作)onProgressUpdate
(在 doInBackground 方法里面调用 publishProgress 方法不断的更新进度值,不断的调用该方法,运行在 UI 线程)onPostExecute
(在 publishProgress 执行完以后把返回值传递给这个方法,在主线程中执行)
2.5 一些不需要重写的重要方法
execute(Params... params
) 用于开启AsyncTask
的执行cancel(boolean mayInterruptIfRunning)
用于标记本次任务的状态我取消状态,并不是真正的去终止任务,而是把mCancelled
设置为true
,所以我们还需要在doInBackgroud
中根据mCancelled
的值来做判断进而终止任务的执行,这个类似于我们要终止一个线程,如果调用interrupt()
方法 只是标记该线程中断,需要在线程内部进行标记判断来终止线程。
2.6 一些使用注意事项
AsyncTask
的实例必须在UI
线程创建execute(Params... params)
方法必须在UI
线程调用- 不要手动调用
onPreExecute()、doInBackground(Params... params)、onProgressUpdate(Progress... values)、onPostExecute(Result result)
这几个方法。 doInBackground(Params... params)
运行在子线程,不要写更新UI
的操作- 一个
AsyncTask
实例只能执行一次,如果第二次执行会报java.lang.IllegalStateException: Cannot execute task :the task has already been executed (a task can be executed only once)
异常。 - 执行顺序为
onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
三、AsyncTask 的使用
3.1 实例展示
新建 AsyncTaskActivity
:
public class AsyncTaskActivity extends AppCompatActivity {
private static final String TAG = "AsyncTaskActivity";
private Button mButton;
private Button mCancel;
private ProgressBar mProgressBar;
private ImageView mImageView;
private MyAsyncTask mAsyncTask;
String mUrl = "https://image.uisdc.com/wp-content/uploads/2014/07/085625KMV.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);
mButton = findViewById(R.id.download);
mCancel = findViewById(R.id.cancel_download);
mProgressBar = findViewById(R.id.progress);
mImageView = findViewById(R.id.image);
mAsyncTask = new MyAsyncTask();
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAsyncTask.execute(mUrl);
}
});
mCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消一个正在执行的任务,onCancelled方法将会被调用
mAsyncTask.cancel(true);
// mAsyncTask.onCancelled("取消了");
}
});
}
class MyAsyncTask extends AsyncTask<String, Integer, String> {
private OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.readTimeout(15000, TimeUnit.MILLISECONDS)
.writeTimeout(15000, TimeUnit.MILLISECONDS)
.build();
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG, "onPreExecute: 准备下载 运行在 UI 线程 ");
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d(TAG, "onPostExecute: 下载完成 运行在 UI 线程 ");
Bitmap bitmap = BitmapFactory.decodeFile(s);
mImageView.setImageBitmap(bitmap);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//Log.d(TAG, "onProgressUpdate: 下载进度 运行在 UI 线程 " + values[0]);
mProgressBar.setProgress(values[0]);
}
/**
* 后台下载图片,最终返回图片的本地地址
* @param strings
* @return
*/
@Override
protected String doInBackground(String... strings) {
Log.d(TAG, "doInBackground: 后台开始下载 运行在子线程 " + strings[0]);
Request request = new Request.Builder()
.url(strings[0])
.build();
InputStream inputStream = null;
FileOutputStream fileOutputStream = null;
long downloadSize = 0;
long totalSize;
try {
Response response = mOkHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
inputStream = response.body().byteStream();
totalSize = response.body().contentLength();
File file = new File(Environment.getExternalStorageDirectory(), "test1111.jpg");
fileOutputStream = new FileOutputStream(file);
byte[] buffer = new byte[512 * 1024];
int len;
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
fileOutputStream.write(buffer, 0, len);
fileOutputStream.flush();
downloadSize += len;
float percent = (float) downloadSize / totalSize;
publishProgress((int) (percent * 100));
}
return file.getAbsolutePath();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
if (null != fileOutputStream) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onCancelled(String s) {
super.onCancelled(s);
Log.d(TAG, "onCancelled: " + s);
}
@Override
protected void onCancelled() {
super.onCancelled();
Log.d(TAG, "onCancelled: 下载取消");
}
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<Button
android:id="@+id/download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="下载" />
<Button
android:id="@+id/cancel_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消下载" />
<ProgressBar
android:id="@+id/progress"
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/image"
android:layout_width="320dp"
android:layout_height="320dp"
android:layout_gravity="center"
android:layout_marginTop="24dp" />
</LinearLayout>
运行效果:
可以看到 AsyncTask
的使用是很简单的,比我们写 Thread
然后再写 Handler
简单了很多。
四、源码分析
上面讲解了 AsyncTask
的概念、方法、使用等,下面就仔细分析下,AsyncTask
到底是如何运行的。
4.1 先看看 AsyncTask 的成员变量
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
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;
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;
}
可以看到,上面定义的都是和线程池有关的,也就是说 AsyncTask
内部的线程是通过线程池来管理的。
public abstract class AsyncTask<Params, Progress, Result> {
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
。接着往下看:
public abstract class AsyncTask<Params, Progress, Result> {
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
// 这里又创建了一个线程池,主要是用于多个耗时任务的时候,使其按照顺序排列
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;
// AsyncTask 的静态内部类,继承于 Handler 用于从子线程接收到消息,然后切换到主线程
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
// 当前状态
private volatile Status mStatus = Status.PENDING;
// 是否取消
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
// 主线程的 Handler
private final Handler mHandler;
}
这里主要定义了一些关键的成员常量,来看几个主要的
4.1.1 SERIAL_EXECUTOR
// 这里又创建了一个线程池,主要是用于多个耗时任务的时候,使其按照顺序排列
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
SerialExecutor
是 AsyncTask
的一个内部类:
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
实现了 Executor
接口,内部维护了一个队列 ArrayDeque<Runnable> mTasks
,以及一个成员变量 Runnable
。
在 execute
方法中,首先通过 mTasks.offer
方法把新建一个 Runnable
对象添加到队列尾部,然后再 run
方法里面去执行传递给 execute
方法的 Runnable
对象,
if (mActive == null) {
scheduleNext();
}
如果当前的 mActive
是空的就去执行 scheduleNext()
,scheduleNext()
方法里面其实就是通过 AyyayDeque
的 pool()
方法从任务队列 mTas``ks 中删除队列中的第一个元素,并把返回值赋值给 mActive
,如果第一个元素不为空(也就是 mTasks
中有任务还未执行,就去调用 THREAD_POOL_EXECUTOR
(真正的线程执行者)去执行这个任务。)如果执行 mTasks
的 pool
方法返回的值为 null
,那么什么都不做。
emmmmmmmmmmmmmmmmmmmmmmmmm
可能有点晕,简单再说下:
也就是说这个 SerialExecutor
存在的意义并不是用来具体的执行任务的,,它内部维护这一个队列,当调用他的 execute()
方法执行任务的时候,会先向其内部的队列 mTasks
尾部添加一个任务 (Runnable
),然后判断其内部的 mActive
是不是有任务在执行,如果没有任务在执行,那么就从内部任务队列中取出一个任务去执行,直到任务执行完毕。
也就是说,SerialExecutor
对象,它只负责维护一个队列,让任务能够按照一定的顺序执行,真正执行任务的是在静态代码块中创建的线程池 THREAD_POOL_EXECUTOR
。也就是说在 THREAD_POOL_EXECUTOR
这个线程池中,一次只能够执行到一个任务。
4.1.2 mCancelled 标记任务是否取消
4.1.3 mTaskInvoked 标记任务是否执行
4.1.4 mStatus 标记任务状态
4.1.5 sHandler 任务线程的 Handler
4.1.6 mHandler 主线程的 Handler
4.2 从入口看起 execute(Params... params)
我们先从运行任务的入口 execute 方法看起,源码如下:
@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;
}
可以看到首先会对 mStatus
进行判断,只要不是 PENDING
(待执行) 状态的,都会抛出异常,而只要一个 AsyncTask
对象执行了 execute
方法,就会执行 mStatus = Status.RUNNING;
那么其状态就会被设置成 RUNNING
,这也就解释了为什么一个 AsyncTask
对象只能被执行一次。
onPreExecute();
这段代码运行在主线程中,验证了前面说的 onPreExecute
方法是在异步任务执行前调用的,如果重写了 onPreExecute()
;就会在任务开始前进行调用。
mWorker.mParams = params;
这行代码是把我们传入的参数 赋值给 mWorker
中的变量 mParams
,我们先来看下 mWorker
是什么鬼?
private final WorkerRunnable<Params, Result> mWorker;
可以看到 mWorker
是 WorkerRunnable
的对象,
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
WorkerRunnable
又是 AsyncTask
的静态内部抽象类,并且实现了 Callable
接口,并且内部存储了我们传入的参数。那么他是什么时候赋值的呢?继续往下看。
继续看下面一行:
exec.execute(mFuture);
exec
就是 AsyncTask
的 execute
方法我们传进来的线程池。mFuture又是什么鬼?
mFuture
是 FutureTask<V>
的对象, FutureTask<V>
实现了 RunnableFuture<V>
接口,而RunnableFuture<V>
接口又继承于 Runnable
和 Future<V>
,所有这里的 mFuture
可以实现在子线程中执行任务并且得到最终执行返回值的功能,继续往下看
我们创建 AsyncTask 对象时候,会调用其参构造方法:
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
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
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return 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
和 mFuture
都是在这里创建的。
先来看 mWorker
:
执行 mWorker
中的 call
方法的时候,代码已经处于子线程了,这时会把 mTaskInvoked
设置为 true
,并且把参数传递给 doInBackground
方法执行,所以说 doInBackground
是运行在子线程中的,我们可以做一些耗时任务。
最终会把 doInBackground
方法的到的返回值传递给 postResult
方法。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在 postResult
方法中,通过 getHandler()
把完成的消息传递出去,看下: getHandler()
这个方法:
private Handler getHandler() {
return mHandler;
}
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
没错,这个Handler
就是通过主线程的 Looper
来创建的,是属于主线程的 Handler
。也就是说,在主线程的 mHandler
接收到消息之后,以后的代码都是运行在主线程中了。
再来看下这个通过主线程 Handler
发送出的 Message
内容:
what
的值为MESSAGE_POST_RESULT
,,这个是一个常量obj
的值为new AsyncTaskResult<Result>(this, result)
AsyncTaskResult :
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
AsyncTaskResult
对象里面存储了 AsyncTask
对象和最终的计算结果。
那么在哪里接收这个消息呢?
对的,直接搜索 MESSAGE_POST_RESULT
在哪使用的就行了,我找到了:
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;
}
}
}
就是这个 Internalhandler
,它继承了 Handler
,所以有处理消息的能力。
这里先拿到 Message.obj
的值赋值给 AsyncTaskResult<?>
的对象 result
,然后把 result
对象中的 mData
传递给 mTask
(也就是AsyncTask
) 的 finish
方法;
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这里先判断任务有没有取消掉,如果取消掉了,那么就调用 onCancelled(result)
;,不会调用 onPostExecute(result)
;反之亦然,这也说明了 onCancelled(result)
和 onPostExecute(result)
; 肯定不会同时调用的。
最后再把当前任务的状态设置为完成 Status.FINISHED
。
与之类似的是,我们获取当前任务的进度值也是通过该种机制来进行的,有兴趣的可以去看下,这里就不多说了。
接下来看下 mFuture
:
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
是实现了 Callable
接口的,在创建 mFuture
的构造参数中可以接收一个 Callable
类型的对象,这里会把 mWorker
传递进去,然后在执行 mFuture
的 do``ne 方法时调用 postResultIfNotInvoked(get());
方法。
get()
表示获取 mWorker
的 call
的最终计算返回值,即最终的 Result
。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
最终会根据 mTaskInvoked.get()
来获取该任务是否执行,如果没被执行,那么就执行 postResult(result);
但是,我们前面在初始化 mWorker
的时候,会先执行了 mTaskInvoked.set(true);
,所以,这里的 postResult(result)
; 正常情况下是执行不到的。
到这里大概已经清楚 mWorker
和 mFuture
是干嘛的了?
但是需要注意的是,上面只是初始化了 mWorker
和 mFuture
,并没有去执行 mWorker
中的 call
方法和 mFuture
的 done
方法。
那 mWorker
中的 call
方法和 mFuture
的 done
方法什么时候执行呢?
调用 exec.execute(mFuture)
了以后,就会去调用 mWorker
中的 call
方法,call
方法中会去调用 doInBackground
方法去执行异步任务,然后任务执行完毕以后,会调用 mFuture
的 done
方法,表示任务执行结束,然后就在 done
方法中调用 get()
方法获取执行结果传递给 postResultIfNotInvoked(get())
了。
五、最后
至此,一次完整的异步任务执行流程就分析完毕了,相信你对 AsyncTask
应该有了更加清晰的认识了。
下篇文章我们来看下 IntentService
的相关代码。