HandlerThread是Thread的子类,主要特点就是为我们主动封装了Looper,这样我们就可以和Handler结合在一起使用,利用Handler的消息机制原理为我们更加有序高效的管理Thread通信和其它逻辑,这也为什么我们通常在自定义的Thread中使用Handler的原因。
首先我们先了解一下自定义Thread如何使用Handler,下面是完整Kotlin代码:
class MyThread() : Thread() {
var handler: Handler? = null;
override fun run() {
super.run()
Looper.prepare();//准备looper
initHandler();
//处理耗时操作,比如请求API,大量数据读写等
Log.d("MyThread", "MyThread-开始处理耗时逻辑");
getWeather()
//开始处理消息,确保在loop方法放在最后,因为这是无限循环,后面的代码不会执行
//当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行
//逻辑运行完成后调用quit或者quitSafely退出,否则会造成这个Thread会一直运行,从而造成内存泄露
Looper.loop();
}
fun initHandler() {
handler = Handler(object : Handler.Callback {
override fun handleMessage(msg: Message?): Boolean {
when (msg!!.what) {
0 -> {
Log.d("MyThread", "MyThread-Success");
handler!!.sendEmptyMessage(2)
}
1 -> {
Log.d("MyThread", "MyThread-Failed");
handler!!.sendEmptyMessage(2)
}
2 -> {
Log.d("MyThread", "MyThread-结束");
handler!!.looper.quitSafely();
}
}
return true;
}
})
}
fun getWeather() {
var response = HttpUtils.getInstance().requestWeather();
if (response != null) {
var result: String = response.body()!!.string();
Log.d("MyThread", result);
handler!!.sendEmptyMessage(0)
} else {
handler!!.sendEmptyMessage(1)
}
}
}
新建的Thread中没有Looper,需要我们指定或者使用Looper.prepare()新建一个,看一下prepare的源码就知道了:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//新建Looper
}
如果没有指定Looper就创建Handler会报以下Exception:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.(Handler.java:204)
at android.os.Handler.(Handler.java:132)
总结以上代码我们发现如果我们在自定义的Thread中使用Handler需要我们主动去实现Looper相关的功能,还是比较麻烦的,所以Android已经为我准备了HandlerThread类,已经封装好了Looper的逻辑,看一下Handler Thread中run方法的源码:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
Handler Thread使用起来也非常简单:
//var为可变变量 和val为常量相当于final
val handlerThread = HandlerThread("MyHandler");
var handler: Handler? = null;
fun initHandlerThread() {
handlerThread.start();
val looper = handlerThread.looper;
//handler运行在handlerThread线程中,不能直接操作UI
handler = object : Handler(looper) {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
when (msg!!.what) {
0 -> {
getWeather()
}
1 -> {
Log.d("HandlerThread", "HandlerThread-退出");
handlerThread.quitSafely()//安全推送线程
}
}
}
};
}
/**
* 因为getWeather方法运行在handlerThread线程中,所以不用担心ANR,使用同步方式请求API
*/
fun getWeather() {
var response = HttpUtils.getInstance().requestWeather();
if (response != null) {
var result: String = response.body()!!.string();
Log.d("HandlerThread", result);
}
handler!!.sendEmptyMessage(1)
}
在使用HandlerThread时我们一定要注意需要我们及时退出Looper,否则容易造成内存泄露。Looper退出的方法有两个quit和quitSafely,HandlerThread中源码如下:
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
再看一下Looper中的源码:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
都是调用MessageQueue的quit的方法,只不过一个传了false,一个传了true,区别就是quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。
quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。如果有兴趣可以参考一下下面的源码:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}