上一篇分析了开启多线程处理耗时任务的一些方法,但是就Android开发而言很多时候并不会直接使用Thread和Runnable,而是用AsyncTask、IntentService或者HandlerThread来实现在主线程开启新的子线程进行耗时任务并且等任务结束还能回到主线程更新UI。下面具体分析一下:
1、AsyncTask
AsyncTask是Google官方提供的一个轻量级的异步任务框架,即可以在主线程使用它发起耗时任务还可以在它的回调方法中返回任务执行进度和最终结果方便的更新UI界面。
这个类提供了三个泛型参数<Params, Progress, Result>,分别是执行任务所需的参数类型、任务执行进度的类型以及任务执行结果的返回类型。如果不需要具体参数就用Void代替,一个不需要一个Void都不需要就三个Void。
重要方法:
doInBackground
这个方法是在子线程执行的,在这里您可以做耗时操作。它的形参对应的是上面提到的第一个泛型参数Params。
这个方法从被回调的时序关系来说他不是最早的,但是我们把它放到第一个来分析是有以下几点原因:
第一:几个常用方法中他是唯一一个在子线程执行的,咱用AsyncTask为了啥不就是为了做点耗时任务嘛,所以他很重要
第二:从代码本身来看doInBackground是个抽象方法,是子类唯一必须要实现的方法,其他都不是抽象方法 是单纯重写父类方法,写不写看需求
第三:需要主动调用publishProgress方法传递进度值,否则后面的另一个重要方法onProgressUpdate不会被调用
第四:需要在return中返回计算结果给后面的另一个重要方法onPostExecute
onPreExecute:
这个方法是在主线程执行的,他在doInBackground之前调用,因此用来做一些异步任务执行前的准备工作。所以从时序上来说他是四个方法中最早执行的。
onProgressUpdate:
这个方法是在主线程执行的,当异步任务执行进度改变时被调用,此处可以用来更新进度条。
onPostExecute:
这个方法是在主线程执行的,当异步任务执行结束时被调用,此处可以处理执行结果更新UI。
下面是一个demo,因为没有做实际的耗时任务因此我在doInBackground方法手动加了一个10秒的延时:
/**
* AsyncTAsk
* 范型分别代表的是params请求参数、progress进度条、result返回结果
*/
//todo 范型参数可以在调用excute方法时传入的,也可以通过构造方法
public class AsyncTaskService extends AsyncTask<String,Integer,Object> {
private static final String TAG = AsyncTaskService.class.getSimpleName();
private Activity activity;
private ContentResolver contentResolver;
public AsyncTaskService (Activity activity){
this.activity = activity;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.e(TAG, "onPreExecute: " );
if (contentResolver == null && activity != null){
contentResolver = activity.getContentResolver();
}
}
//在后台的线程,可以执行耗时操作
//todo doInBackground是个抽象方法必须实现的,其他都不是抽象方法 是单纯重写父类方法
@Override
protected Object doInBackground(String... voids) {
Log.e(TAG, "doInBackground: "+voids[0]);
String[] projection = {MediaStore.Images.Thumbnails._ID, MediaStore.Images.Thumbnails.DATA};
//耗时查询系统相册 缩略图和非缩略图
// contentResolver.query()
//模仿进度条,必须主动调用否则onProgressUpdate不会执行
publishProgress(0);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(1);
return voids[0]+"加盐";
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.e(TAG, "onProgressUpdate: "+values[0] );
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
Log.e(TAG, "onPostExecute: o= " + o.toString() );
}
@Override
protected void onCancelled(Object o) {
super.onCancelled(o);
Log.e(TAG, "onCancelled: o="+ o.toString());
}
}
运行日志:
2024-03-04 16:00:08.102 10433-10433/com.example.testdemo3 E/AsyncTaskService: onPreExecute:
2024-03-04 16:00:08.104 10433-10522/com.example.testdemo3 E/AsyncTaskService: doInBackground: AsyncTask传餐
2024-03-04 16:00:08.104 10433-10433/com.example.testdemo3 E/AsyncTaskService: onProgressUpdate: 0
2024-03-04 16:00:18.145 10433-10433/com.example.testdemo3 E/AsyncTaskService: onProgressUpdate: 1
2024-03-04 16:00:18.146 10433-10433/com.example.testdemo3 E/AsyncTaskService: onPostExecute:
通过日志可以看出AsyncTask执行异步任务的逻辑是先执行onPreExecute方法让我们做一些准备工作,然后doInBackground方法被回调做耗时任务,需要主动调用publishProgress方法,然后onProgressUpdate被回调,doInBackground中伪造了十秒延时后再次主动调用publishProgress方法onProgressUpdate再次被回调,最后任务执行结束以后onPostExecute被回调返回执行结果。
注意:
AsyncTask对象必须在主线程创建,并且开启任务的execute方法也必须在主线程执行。还有啊,一个AsyncTask对象只能调用一次execute方法。
2、IntentService
IntentService是四大组件之一Service的子类专门解决它不能执行耗时任务处理异步请求,
3、HandlerThread
HandlerThread其实相对小众且陌生的一个类, HandlerThread继承Thread类,本质上就是一个普通Thread只不过内部建立了Looper,简化了像下面这种子线程与子线程之间的通信。同样用来处理子线程和主线程之间相互交互的场景。