浅析HandlerThread
背景
首先呢?HandlerThread面试的时候有的会问,但是面试官不直接问你是否知道HandlerThread以及用途和实现?面试官会问你:面试必问的一个题目:
handler的消息机制等一系列问题,如果你说的还算可以,那么问题来了?
接下来会问你假如在一个子线程(工作线程)中怎么使用handler?嘻嘻 ,如果你知道handler的消息机制,那么这个问题很好回答了,代码如下:
private final class WorkThread extends Thread {
private Handler mHandler;
public Handler getHandler() {
return mHandler;
}
public void quit() {
mHandler.getLooper().quit();
}
@Override
public void run() {
super.run();
//创建该线程对应的Looper,
// 内部实现
// 1。new Looper()
// 2。将1步中的lopper 放在ThreadLocal里,ThreadLocal是保存数据的,主要应用场景是:线程间数据互不影响的情况
// 3。在1步中的Looper的构造函数中new MessageQueue();
//其实就是创建了该线程对用的Looper,Looper里创建MessageQueue来实现消息机制
//对消息机制不懂得同学可以查阅资料,网上很多也讲的很不错。
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
}
};
//开启消息的死循环处理即:dispatchMessage
Looper.loop();
//注意这3个的顺序不能颠倒
Log.d("WorkThread", "end");
}
}
这里我们可以测试下在另外一个子线程里通过WorkThread的getHandler给它发消息看下结果:
public void send(View view) {
new SendThread(mWorkThread.getHandler()).start();
}
private final class SendThread extends Thread {
private Handler mHandler;
SendThread(Handler handler) {
this.mHandler = handler;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 3; i++) {
mHandler.sendEmptyMessage(0x1);
SystemClock.sleep(1000);
}
}
}
log:可以看到收到了消息并打印了是在非主线程
02-15 10:45:19.290 22660-22738/com.gxz.study D/WorkThread: false,1
02-15 10:45:20.290 22660-22738/com.gxz.study D/WorkThread: false,1
02-15 10:45:21.291 22660-22738/com.gxz.study D/WorkThread: false,1
这里有同学问 小伙子,你这里不对, Log.d("WorkThread", "end");没打印。是的,Looper.loop();为阻塞函数,只有当调用mHandler.getLooper().quit()或者quitSafely()方法后,loop才会中止.那2个方法有毛区别呢?
当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。
需要注意的是Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。
HandlerThread的用法
前面扯远了,但是你必须知道的东西。那就有同学问了好麻烦啊上面的,假入我以后在工作中用的话我还得注意 Looper.prepare(); new mHandler(); Looper.loop();的顺序。呵呵。。。
哈哈,同学,这个HandlerThread就是解决这个的。google工程师早知道你会这么问。
我们先看怎么用,我们把上面的测试例子用HandlerThread实现然后在分析内部原理。
//1.初始化,参数为名字,也就是线程的名字,后面我们会结合源码来看
mHandlerThread = new HandlerThread("WorkThread");
//必须调用start方法,因为HandlerThread继承自Thread来启动线程
mHandlerThread.start();
//初始化Handler,只是传递了一个mHandlerThread内部的一个looper
mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
}
};
//2.使用
public void send(View view) {
new SendThread(mHandler).start();
}
private final class SendThread extends Thread {
private Handler mHandler;
SendThread(Handler handler) {
this.mHandler = handler;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 3; i++) {
mHandler.sendEmptyMessage(0x1);
SystemClock.sleep(1000);
}
}
}
//3.别忘记释放,停止Looper消息循环,内部还是调用了looper的quit,及时释放防止内存泄漏哦!!
mHandlerThread.quit();
结果(一毛一样哦):
02-15 11:11:24.738 13536-13787/com.gxz.study D/WorkThread: false,1
02-15 11:11:25.739 13536-13787/com.gxz.study D/WorkThread: false,1
02-15 11:11:26.740 13536-13787/com.gxz.study D/WorkThread: false,1
因此
- 如果我们有耗时的任务处理可以通过HandlerThread获取looper,looper进而构造Handler,然后通过Handler的post(Runnable r)在handleMessage里进行处理耗时处理。
- 很方便进行线程间的通信。
HandlerThread的源码分析
- 原理:HandlerThread其实是extends Thread,内部的run方法调用了Looper.prepare()方法和 Looper.loop();哈哈,和我们之前写的第一种方法一样。
public class HandlerThread extends Thread {
int mPriority;//线程优先级
int mTid = -1;//线程id
Looper mLooper;//Looper对象
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
- 对于线程,我们还是主要看run方法,源码很短,直接全部贴了。我们清楚的看到了Looper.prepare和Looper.loop的创建,这里和最开始说的一样。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
//后面会分析
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
但是这多了一个onLooperPrepared()方法,这里可以根据名字看出来,当Looper.prepare()创建完后,我们可以做一些初始化的东西,这当然是在子线程里,我们看下这个方法的实现。空实现,如果你要做些初始化的准备工作可以extends HandlerThread重写onLooperPrepared方法即可。
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
- 退出/结束方法:quit()方法或quitSafely()方法。实际上是调用了looper.quit方法和looper.quitSafely方法,上文中的区别也说到了。
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;
}
- 最后还有一点,就是在上面的例子中我们构造Handler的时候传入了Looper,通过HandlerThread的getLooper(),源码如下。这里如果线程没有start活着一些其它原因,该线程没有处于一种存活的状态会返回null,
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
该函数有可能阻塞,目的是为了防止获取的looper对象为空,使用了wait方法,并且使用了局部synchronized,锁住了当前的对象,那什么时候唤醒等待呢?当然是初始化完该线程关联looper对象的地方,也就是run方法。也就是你构造完HandlerThread必须调用start方法。对于synchronized和notifyAll和wait一些线程同步处理的操作,可以参考一些资料。
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
结束
这里我们学习了HandlerThread类的简单用法以及内部源码以及实现原理。
同时也介绍了一些面试必问handler的消息机制。so,当再有面试官问你文章最开始的在一个子线程(工作线程)中怎么使用handler的问题,你可以说下怎么使用,同时最后说一个android提供的类-HandlerThread也很不错哦!!