Android 多线程:AsyncTask理解和使用总结
一、AsyncTask介绍
1.1 作用:
轻量级的异步类,同时方便在子线程更新UI。
1.2 原理:
封装了Handler和两个线程池。AsyncTask的源码链接
-
线程池THREAD_POOL_EXECUTOR:通过ThreadFactory和一些参数实例化线程池
THREAD_POOL_EXECUTOR
用于执行任务。 -
线程池SERIAL_EXECUTOR:内部类SerialExecutor实现Executor也是一个线程池,实例化对象
SERIAL_EXECUTOR
执行SerialExecutor定义的execute
方法来串行(一个一个按顺序)排队任务并使用THREAD_POOL_EXECUTOR
来执行任务。 - AsyncTask创建时会实例化一个WorkerRunnable对象mWorker和一个FutureTask对象mFuture。FutureTask是一个并发类,充当Runnable的作用。AsyncTask执行任务时把mFuture作为排队线程池SerialExecutor的
execute(final Runnable r)
的参数传递,接着会串行排队并使用工作线程来处理实际任务。 -
mFuture对象创建时把mWorker对象作为参数传递,在mFuture在执行任务
run()
时会调用mWorker的call()
方法,因此call()
方法是在线程池中进行的。 -
主线程的Handler:然后
call()
方法里面接着调用result = doInBackground(mParams);
并返回Result对象,这个过程中任务被取消会catch跳出设置AsyncTask结束。call()
方法最后postResult(result);
获取主线程的Handler发送消息并根据消息类型判定更新UI或者结束AsyncTask。
具体参考:源码解析Android中AsyncTask的工作原理
1.3 参数以及核心函数
AsyncTask是一个泛型类,在android.os
包下
泛型内三个参数定义的是我们需要的数据的类型,如果不需要传递参数,可以用Void来代替,以下是具体含义:
- Params:参数的类型;
- Progress:后台任务的执行进度类型;
-
Result:后台任务返回结果的类型。
AsyncTask还提供了4个核心方法:
-
protected void onPreExecute()
:在主线程执行,异步任务执行前调用做准备工作; -
protected abstract Result doInBackground(Params... var1)
:在线程池中执行,用于执行异步任务。Params表示异步任务输入参数,可以在此方法中通过publishProgress方法来更新任务进度,publishProgress方法又调用onProgressUpdate方法实现主线程进度更新。这个方法返回Result给onPostExecute方法。 -
protected void onProgressUpdate(Progress... values)
:在主线程执行,后台任务执行进度发生变化会调用此方法。 -
protected void onPostExecute(Result result)
:在主线程执行,异步任务执行后调用,result参数是由doInBackground返回的。
这四个方法执行的顺序为:onPreExecute(准备)-->doInBackground(后台异步)-->onPostExecute(结果处理)。
还有一个有用的方法:protected void onCancelled()
:当异步任务取消时调用,而这时候onPostExecute不会被调用。
二、AsyncTask使用
2.1 AsyncTask使用示例:
(1) 创建Activity,布局两个按钮用来开始和取消AsyncTask,一个TextView来显示状态。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始下载"/>
<Button
android:id="@+id/btn_cancel"
android:layout_below="@id/btn_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消下载"/>
<TextView
android:id="@+id/tv_statue"
android:layout_below="@id/btn_cancel"
android:textSize="15sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
(2) 创建内部类自定义MyAsyncTask继承AsyncTask,重写四个主要方法:
//第一个参数:Params参数类型:String类型
//第二个参数:Progress执行进度类型:Object类型
//第三个参数:Result参数类型:Long类型
private class MyAsyncTask extends AsyncTask<String,Object,Long>{
...
@Override
protected void onPreExecute() {...}
@Override
protected Long doInBackground(String... strings) {...}
@Override
protected void onProgressUpdate(Object... values) {...}
@Override
protected void onPostExecute(Long aLong) {...}
@Override //任务取消调用
protected void onCancelled() {...}
(3)开始按钮调用创建MyAsyncTask并执行downloadTask.execute(urls)
传入参数。
mDownButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String[] urls = {
"http://www.jianshu.com/p/d3984e7e204f",
"http://www.jianshu.com/p/8e3cfb87f35e",
"http://www.jianshu.com/p/4fbc2ea2e02b"
};
downloadTask = new MyAsyncTask();
// 这里的url也可以分成一个一个参数传递
downloadTask.execute(urls);
}
});
(4) 取消按钮调用downloadTask.cancel(true)
停止MyAsyncTask。
//取消按钮点击事件
downloadTask.cancel(true);
(5) doInBackground(String... strings)
方法循环执行下载任务并返回result,是自己定义的二维数组,第一个是字节数,第二个是文章标题。
protected Long doInBackground(String... strings) {
long totalByte = 0;
for (String url:strings){
// downloadFile方法只是一个简单的下载字节流
// 返回的Object[] result是二维数组,一个是下载的字节数,一个是标题
Object[] result = downloadFile(url);
int byteCount = (int) result[0];
totalByte += byteCount;
//传递进度
publishProgress(result);
// 异步任务调用取消则跳出for循环
if(isCancelled()){
break;
}
}
return totalByte;
}
(6) 然后使用publishProgress(result);
把数据传递给onProgressUpdate(Object... values)
方法来更新UI,这个方法已经切换到了主线程,所以可以更新UI。AsyncTask完成后onPostExecute(Long aLong)
方法自动调用再次来更新状态。
@Override
protected void onPostExecute(Long aLong) {...}
看一下效果,源码我会在后面放出,取消下载请自行测试。
AsyncTaskDemo.gif
2.2 AsyncTask使用注意:
- AsyncTask的类必须在主线程加载。
- AsyncTask的对象必须在主线程创建。
- execute方法必须在UI线程调用。
- 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则报运行时异常。
- 在Android1.6之前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始使用线程池处理并行任务,但是从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程串行执行任务。尽管如此,在Android3.0以及后续的版本中,我们仍然可以通过AsyncTask的executeOnExecutor方法来并行地执行任务。
三、AsyncTask优点和缺点
3.1 优点:
- 简单快捷,使用方便。
- UI更新及时,过程可控。
3.2 缺点:
多个异步操作需要更新UI时,就变得麻烦起来。
3.3 其它问题:
参考文章:AsyncTask的缺陷和问题
目录文章:
Android多线程:理解和简单使用总结