Android异步任务处理
Android主线程(UI线程)不准执行异步任务,以免阻塞主线程。所以异步任务一定是在工作线程中完成,然后,通知主线程,进行返回结果,刷新UI等操作。
1、使用handler
使用handler可以很方便的进行主线程与工作线程的交互,当在线程里创建handler时,handler会自动绑定当前线程
重写@Overried handlerMessage(),可以处理Message。
/**
*
*
*/
@Override
public void handleMessage(@NonNull Message msg) {
//根据msg.what执行相应命令(不设置,默认为0),msg.obj携带结果信息
switch (msg.what){
case 0:
…执行相应操作
break;
default:
}
}
常见的做法是在主线程中创建handler,在工作线程中处理完异步任务后发送Message,主线程重写handlerMessage()里处理返回结果。
使用handler进行异步处理时,容易产生内存泄漏问题,固然可以通过将handler设置为静态内部类,解决这个问题,但是将handler设置为静态内部类后就无法引用内部成员变量。内存泄漏原因是activity要销毁时,消息队列里消息没处理完,Message Queue默认引用handler,handler默认引用activity。
所以推荐的做法是:在onDestory()阶段,清空消息队列,取消handler引用。
protected void onDestroy() {
super.onDestroy();
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
(参考资料)
https://juejin.im/post/5a692377518825734e3e71ab
2、使用IntentService(异步任务处理服务)
使用IntentService,IntentService其实是封装了service和handler的类,所以和上述类似,它处理消息是在@Overried
onHandleIntent()
根据传入的intent执行相应的异步处理
IntentService其内部维护一个消息队列,根据传入的intent的时间,先来先服务。前一项服务没处理完,后一项服务不会开始。
官网推荐的IntentService与Activity通信的方式是localBroadcast。
相比handler,其主要优点在于可以在后台执行,不受Activity生命周期影响。(只能通过startActivity()启动IntentService,可以像普通service一样绑定activity,通过Binder通信)
如果说消息队列里没消息了,那么service会暂停,内存不够的情况下有可能被系统杀死。
3、使用handlerThread
听名字就可以猜出来,一个继承了Thread的handler处理类,其内部维护一个消息队列,重写@Overried handlerMessage()处理消息,对应于主线程。
一些情况下,我们需要工作线程处理消息,那么我们就创建一个handlerThread,根据传入消息,执行异步任务。
比较好的理解方式:主线程可以有个handleMessage,我工作线程也想有个,但是新建一个Thread,设置Looper什么的太麻烦,于是新建一个HandlerThread,一些准备工作都做好了,像主线程一样创建handler绑定线程,重写@Overried handlerMessage()处理消息就OK了!
其实创建一个Hhread,手动引用创建一个Looper的效果和创建HandlerThread效果一样
需要注意:为了防止内存泄漏,要像在主线程里的handler一样,在onDestory()阶段,
清空消息队列,取消handler引用。
/**
*
* workHandler: 绑定工作线程的handler
* mHandler:绑定主线程的handler
*/
protected void onDestroy() {
super.onDestroy();
if (workHandler != null) {
workHandler.removeCallbacksAndMessages(null);
workHandler = null;
}
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
4、使用AsyncTask
使用AsyncTask,AsyncTask是一个封装好的轻量级异步任务处理类,轻量级,顾名思义,一些重量级的异步任务还是自己搞个实现,大部分场景下,AsyncTask够用了。
相比AsyncTask,我更喜欢使用handler+Thread解决异步任务。
一个异步任务使用AsyncTask很方便,多个异步任务使用handler+thread更好。
有一种场景非常适合使用AsyncTask:需要显示进度值的场景,使用这个封装好的类,方便理解,节省代码
轻量级异步处理AsyncTask
/**
* 步骤1:创建AsyncTask子类
* 注:
* a. 继承AsyncTask类
* b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
* c. 根据需求,在AsyncTask子类内实现核心方法
*/
private class MyTask extends AsyncTask<Params, Progress, Result> {
....
// 方法1:onPreExecute()
// 作用:执行 线程任务前的操作
// 注:根据需求复写
@Override
protected void onPreExecute() {
...
}
// 方法2:doInBackground()
// 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
// 注:必须复写,从而自定义线程任务
@Override
protected String doInBackground(String... params) {
...// 自定义的线程任务
// 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(count);
}
// 方法3:onProgressUpdate()
// 作用:在主线程 显示线程任务执行的进度
// 注:根据需求复写
@Override
protected void onProgressUpdate(Integer... progresses) {
...
}
// 方法4:onPostExecute()
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
// 注:必须复写,从而自定义UI操作
@Override
protected void onPostExecute(String result) {
...// UI操作
}
// 方法5:onCancelled()
// 作用:将异步任务设置为:取消状态
@Override
protected void onCancelled() {
...
}
}
/**
* 步骤2:创建AsyncTask子类的实例对象(即 任务实例)
* 注:AsyncTask子类的实例必须在UI线程中创建
*/
MyTask mTask = new MyTask();
/**
* 步骤3:手动调用execute(Params... params) 从而执行异步线程任务
* 注:
* a. 必须在UI线程中调用
* b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
* c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
* d. 不能手动调用上述方法
*/
mTask.execute();
注意:doInBackground()是在工作线程运行的方法,不能调用UI;onPostExecute()是在主UI运行的方法,可以更新UI
总结一下
Thread + 消息机制
优点:使用非常灵活(自己手写的代码一般也是最多的)一般只在Activity里使用,主线程,工作线程均可实现自己的handler机制
缺点:Activity挂了,也就跟着挂了,需要后台运行那就用IntentService
IntentService + ResultReceiver
优点:在后台运行,一般情况下不会被杀死
缺点:官网推荐采用localBroadcast通信,手写代码较多。
AsyncTask
优点:被封装过,需要显示进度条的情况下非常方便,可以串行,也可以并行
缺点:没那么灵活,只能在主线程里创建