AsyncTask知识点总结
前言
1. AsyncTask简介
AsyncTask是Android帮我们封装好的异步处理工具,其本质上是线程池和Handler的封装。
而且不意外的是,线程池用来完成任务,Handler用来通信与更新UI。
2. AsyncTask的基本介绍
AsyncTask在其内部有几个特别重要的东西,首先是三个泛型参数,其次是四个内部方法。
2.1 泛型参数
- Params:我们要执行异步任务之前需要传的参数。
- Progress:在异步任务执行期间负责同步更新任务进度的参数。
- Result:任务执行结束后所返回的结果。
这些参数一般定义这个类的时候就会用到,而且由于是泛型,我们得根据具体需求来决定使用哪些类。
public MyAsyncTask extends AsyncTask<Params, Progress, Result> {
......
}
2.2 四个方法
- onPreExecute():预先执行内容,在正式开始异步任务之前,如果有什么想要先完成的就写在这里,这个方法不能传参数。一般情况下做UI的初始化操作(比如弹出对话框之类的)。这个方法会在主线程中进行,可以更新UI。
- protected Result doInBackground(Params… params):这个就是正式执行的异步任务。传入的参数由于是可变长参数,所以可以同时传多个参数。这是一个有返回值的方法,返回值的类型就是我们定义的Result类型。这个方法在子线程中进行,所以这个方法中不能更新UI。
- onProgressUpdate(Progress… progress):用于同步更新任务的方法,一般来说,如果在异步任务的半途中有什么要通知给主线程的内容,就会调用这个方法。这个方法在主线程中进行,可以更新UI。
- onPostExecute(Result result):在异步任务完成之后,就会调用这个方法。传入的参数就是doInBackground方法中return的值。这个方法在主线程中进行,可以更新UI。
3. AsyncTask的基本用法
不做太过复杂的任务,就以数数从1数到100为例,写一个AsyncTask的例子。
-
New一个新的Android项目,并且有一个EmptyActivity。
-
在activity_main.xml中设置一个进度条Progress Bar,一个TextView。
-
然后在Activity中绑定控件。
TextView textView; ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text_view); progressBar = findViewById(R.id.progress_horizontal); }
-
为了方便区分AsyncTask三个参数的区别,对数字做一个简单封装。
public class Num { int i; public Num(int i){ this.i = i; } public void setI(int i) { this.i = i; } public int getI() { return i; } }
-
然后定义一个AsyncTask,Param是传入的参数填Num类,Progress取Integer,最后的Result取Boolean,写在MainActivity内部。
-
然后再加上四个方法。
public class MainActivity extends AppCompatActivity { ....... class MyAsyncTask extends AsyncTask<Num, Integer, Boolean>{ @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected Boolean doInBackground(Num... nums) { return true; } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); } } }
-
onPreExecute()中做初始化的方法,这边就将textView和ProgressBar初始化一下,因为内部类可以访问外部类,所以可以直接调用。
@Override protected void onPreExecute() { progressBar.setMax(100); progressBar.setProgress(0, true); textView.setText( "0 / 100"); }
-
接下来在doInBackground完成具体数数的逻辑。
@Override protected Boolean doInBackground(Num... nums) { //做个简单的判断 if (nums.length < 1) return false; while (nums[0].getI() < 100){ nums[0].setI(nums[0].getI() + 1); //注意,这里要用publishProgress,不能直接调用onProgressUpdate //publishProgress后,就会执行onProgressUpdate,参数就是现在传入的参数 publishProgress(nums[0].getI()); //每完成一次计数后稍微等一下,不然一下就结束了 try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } } return true; }
-
然后在onProgressUpdate中更新进度。
@Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if (values.length < 1) return; progressBar.setProgress(values[0], true); textView.setText(values[0] + " / 100"); }
-
最后在onPostExecute中,用Toast对任务结果做个总结。
@Override protected void onPostExecute(Boolean aBoolean) { Toast.makeText(MainActivity.this, aBoolean? "complete" : "failed", Toast.LENGTH_SHORT).show(); super.onPostExecute(aBoolean); }
-
最后在onCreate()中调用,为了方便就不做什么点击按钮后开始任务的东西了。然后为了防止内存泄漏,用上WeakPreference。
TextView textView; ProgressBar progressBar; MyAsyncTask myAsyncTask; ReferenceQueue<MyAsyncTask> queue = new ReferenceQueue<>(); WeakReference<MyAsyncTask> weakAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text_view); progressBar = findViewById(R.id.progress_horizontal); myAsyncTask = new MyAsyncTask(); weakAsyncTask = new WeakReference<>(myAsyncTask, queue); myAsyncTask = null; weakAsyncTask.get().execute(new Num(0)); }
-
外部想要调用AsyncTask的结果,需要使用get()这个方法。不过这个方法在出结果之前会造成堵塞。
new Thread(new Runnable() { @Override public void run() { try { //仔细观察第一个log和第三个log之间的时间差,就能体会到堵塞的概念了 Log.i(TAG, "run: " + SystemClock.uptimeMillis()); Log.i(TAG, "run: " + weakAsyncTask.get().get()); Log.i(TAG, "run: " + SystemClock.uptimeMillis()); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
最终代码长这样(不包括Num类):
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
TextView textView;
ProgressBar progressBar;
MyAsyncTask myAsyncTask;
ReferenceQueue<MyAsyncTask> queue = new ReferenceQueue<>();
WeakReference<MyAsyncTask> weakAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
progressBar = findViewById(R.id.progress_horizontal);
myAsyncTask = new MyAsyncTask();
weakAsyncTask = new WeakReference<>(myAsyncTask, queue);
myAsyncTask = null;
weakAsyncTask.get().execute(new Num(0));
new Thread(new Runnable() {
@Override
public void run() {
try {
//仔细观察第一个log和第三个log之间的时间差,就能体会到堵塞的概念了
Log.i(TAG, "run: " + SystemClock.uptimeMillis());
Log.i(TAG, "run: " + weakAsyncTask.get().get());
Log.i(TAG, "run: " + SystemClock.uptimeMillis());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@SuppressLint("NewApi")
class MyAsyncTask extends AsyncTask<Num, Integer, Boolean>{
@Override
protected void onPreExecute() {
progressBar.setMax(100);
progressBar.setProgress(0, true);
textView.setText( "0 / 100");
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (values.length < 1)
return;
progressBar.setProgress(values[0], true);
textView.setText(values[0] + " / 100");
}
@Override
protected Boolean doInBackground(Num... nums) {
if (nums.length < 1)
return false;
while (nums[0].getI() < 100){
nums[0].setI(nums[0].getI() + 1);
//注意,这里要用publishProgress,不能直接调用onProgressUpdate
//publishProgress后,就会执行onProgressUpdate,参数就是现在传入的参数
publishProgress(nums[0].getI());
//每完成一次计数后稍微等一下,不然一下就结束了
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
Toast.makeText(MainActivity.this, aBoolean? "complete" : "failed", Toast.LENGTH_SHORT).show();
super.onPostExecute(aBoolean);
}
}
}
4. 源码解析
有关AsyncTask中的Handler到底做了什么
AsyncTask中,对线程池和Handler进行了封装,我原本以为是那种,getHandler().sendMessage()这样的感觉,但是谁想并不是。
先看内部有关Handler的对象有哪些。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public abstract class AsyncTask<Params, Progress, Result> {
private static InternalHandler sHandler;
private final Handler mHandler;
}
有两个对象,现在不知道InternalHandler是啥,就先看mHandler这个类。
结果mHandler就两个调用,一个赋值一个return。
赋值的这行在构造函数中,也就是说new AsyncTask的时候就会实例化。
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
......
}
简单分析一下,当callbackLooper不等于主线程的Looper的时候,就会new一个子线程的Handler。说是这样说,然而你看看AsyncTask的构造函数列表。
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
......
}
三个构造函数,但是后面两个构造函数中有个@hide,这意味着你无法在正常的开发过程中用上它。也就是说,对于我们而言,只能用无参的构造函数(并且looper肯定是传入null)。
那么回到刚刚的分支条件,绝大部分情况就相当于mHandler = getMainHandler()了。
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
这里就是相当于mHandler = sHandler的一个过程,但是sHandler是一个InternalHandler,并不是普通的Handler,而是AsyncTask内部写的一个继承Handler的静态类。
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内部已经实现了handleMessage的方法,并且其中就有一个我们很熟悉的onProgressUpdate。最后再看看Message是哪里发出的,追踪MESSAGE_POST_PROGRESS这个what值。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
看上去是一行比较复杂的内容,我稍微用平时的语句翻译一下。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
Handler handler = getHandler(); //这个是返回mHandler
Message message = handler.obtainMessage();
message.what = MESSAGE_POST_PROGRESS;
message.obj = new AsyncTaskResult<Progress>(this, values);
handler.sendMessage(message);
}
}
这个publishProgress方法就是我们在doInBackground中用的那个方法。那么也就是说publishProgress这个方法就是发送一个Message给Handler,Handler再来处理,调用onProgressUpdate方法。
隔壁的postResult也是相近内容。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
得出结论:
- 在AsyncTask中,它内部的Handler是已经封装好功能和内容了,我们不用再次实现(也无法实现)。
- doInBackground方法中publishProgress和最后的retrun,其实本质都是发送Message给Handler,再由Handler处理。
- 根据2,可以知道onProgressUpdate方法在主线程执行的原因,是Handler中的handleMessage()方法的内容,而handler绝大部分情况是主线程的Handler。
有关get()方法造成堵塞的原因
这个get()方法是真的不能放在主线程,放进去就容易造成卡机,因为你不知道你的AsyncTask什么时候结束。
@UnsupportedAppUsage
private final FutureTask<Result> mFuture;
......
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
观察一下,mFuture是FutureTask的类型,FutureTask简单来说就是一个有返回值(和Callable相关)的多线程任务,而它的返回值要等任务结束后才能返回。这么看来会堵塞也是正常的。
有关doInBackground为啥在子线程运行
这里就要先看我们平时是怎么开启任务的,我们开启任务一般是调用AsyncTask.execute(Param… params)这个方法。
@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;
}
也就是说,实际上是sDefaultExecutor这个参数,执行了execute()这个方法。当然这个sDefaultExecutor是一个Executors,一个多线程框架,我们平时的newCachedThreadPool()之类的也有用到这个框架。
这就是AsyncTask内部实现的线程池。
参考材料
第一行代码Android(第2版)
p347 - p349
你真的了解AsyncTask?- CSDN
https://blog.csdn.net/majihua817/article/details/51658465
Android面试系列文章2018之Android部分AsyncTask机制篇 - CSDN
https://blog.csdn.net/ClAndEllen/article/details/79346383