在 Android 开发中,主线程(UI线程)不能执行耗时的操作,通常将一些耗时的操作使用异步任务的方式进行处理,简单概括一下:
只能在UI线程操作UI视图,不能在子线程中操作;
不能在UI线程中进行耗时操作,否则会阻塞UI线程,引起ANR、卡顿等问题。
接下来看一下在Android开发中,经常用到的处理耗时操作用的异步任务实现方式及实现原理。
一.Thread
创建一个Thread是最简单直接的方式,在Thread内部去执行耗时的操作,实现方式如下:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//执行耗时操作
}
});
t.start();
上面仅仅是在Thread内部执行耗时的操作,如果在执行玩耗时操作后,需要UI来进行更新,那应该如何操作呢?接下来继续看:
二.Thread+Handler
Android 提供了Handler机制来进行线程之间的通信,可以使用异步方式:Thread + handler 来进行异步任务执行及UI更新,实现方式如下:
详细了解Handler的原理请参考Android Handler原理详解
new Thread() {
public void run() {
//执行耗时操作.....
//更新UI
mUIHandler.sendMessage(mUIHandler.obtainMessage(UIHandler.MSG_UPDATE_UI,2));
}
}.start();
//UI线程Handler
private static class UIHandler extends Handler {
private final static int MSG_UPDATE_UI = 1;
private final WeakReference mFragment;
private UIHandler(HandlerFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
public void handleMessage(Message msg) {
HandlerFragment fragment = mFragment.get();
switch (msg.what) {
case MSG_UPDATE_UI:
fragment.updateUI((int)msg.obj);
break;
default:
break;
}
}
}
从以上可以看到,在Thread内部执行耗时操作后,然后调用Handler来通知主线程更新UI。
这样的话,每次执行耗时请求,都需要new一个Thread,会导致开销过大,可以通过以下方式来进行改进:
子线程内部创建Looper+Handler
new Thread() {
public void run() {
Looper.prepare();
mNoUIHandler = new NoUIHandler(handlerFragment);
Looper.loop();
}
}.start();
//工作线程Handler来执行耗时操作
private static class NoUIHandler extends Handler {
private final static int MSG_HANDLE = 1;
private final WeakReference mFragment;
private NoUIHandler(HandlerFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
public void handleMessage(Message msg) {
HandlerFragment fragment = mFragment.get();
switch (msg.what) {
case MSG_HANDLE:
fragment.handleMsg();
break;
default:
break;
}
}
}
private void handleMsg() {
//执行耗时操作......
//通知主线程更新UI
mUIHandler.sendMessage(mUIHandler.obtainMessage(UIHandler.MSG_UPDATE_UI,1));
}
//启动耗时操作
mNoUIHandler.sendEmptyMessage(NoUIHandler.MSG_HANDLE);
通过以下改善,在Thread内部创建Looper,然后创建Handler,这样的话Thread就不会退出,就不需要频繁创建Thread,此时Handler使用的是子线程创建的looper,从而Handler消息处理(耗时操作)就在子线程里面,执行完耗时操作,通知主线程来更新UI;
这样的话,Thread一直不退出,是不是会造成内存泄露呢?
如果不再使用的话,直接通过mNoUIHandler.getLooper().quitSafely()来让Looper.loop()结束循环,Thread也就退出了。
三.HandlerThread
针对以上问题,Android也早就考虑到了,HandlerThread就出现了,结合了Thread+Looper+Handler进行封装实现,实现如下:
private HandlerThread mWorkHandlerThread;
private Handler mWorkHandler;
//首先创建HandlerThread
mWorkHandlerThread = new HandlerThread("handler thread");
//HandlerThread执行start()
mWorkHandlerThread.start();
//创建Handler将HandlerThread的Looper传入,从而在对应的线程内处理消息
mWorkHandler = new WorkHandler(mWorkHandlerThread.getLooper(), this);
//工作线程的Handler
private static class WorkHandler extends Handler {
private static final int MSG_START = 200;
private ControlService mService;
private WorkHandler(Looper looper, ControlService service) {
super(looper);
mService = service;
}
@Override<