记录-HandlerThread源码分析
上文介绍到了IntentService源码分析,其内部维护了一个HandlerThread的线程,今天周末闲来无事,让我看来看下它内部都做了些什么?
首先回顾下IntentService中HandlerThread的使用
请看下面的贴图
image.png
可以看到,在IntentService的onCreate方法中,实例化了一个HandlerThread,然后调用start()方法启动它,并通过getLooper()方法获取到当前线程的Looper对象,然后new 一个在IntentService中定义的ServiceHandler类。
1.HandlerThread的定义
image.png从上面这张图中我们可以看到源码对HandlerThread的定义:一个持有Looper对象的线程。这个Looper可以用来创建Hanlder(IntentService中也确实是这样使用的),然后说了一下注意事项:就像普通的Thread类一样,必须要调用HandlerThread内部的start()方法(至于为什么,下面会讲到)。
2.直接进入正题,贴源码
package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
/**
* A {@link Thread} that has a {@link Looper}.
* The {@link Looper} can then be used to create {@link Handler}s.
* <p>
* Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason 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;
}
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
可以看到HandlerThread源码并不是很多,主要就是那个几个方法。接下来我们一一剖析。
- 从构造方法开始说起,从上面的源码中我们看到HandlerThread中有两个构造方法。只有一个参数的构造方法,只接收一个name参数,首先调用的super(name)方法,该参数的作用是指定该线程的名字,然后指定了默认的线程优先级:Process.THREAD_PRIORITY_DEFAULT;有两个参数的构造方法跟上面那个类似,只不过我们可以在创建HandlerThread的时候自己指定线程的优先级。一般情况下我们使用默认的线程优先级就可以了。
- 结合IntentService中ServiceHandler的使用我们看到在实例化HandlerThread之后,通过thread对象调用了它的start()方法,前面定义中也有提到
* Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
必须要调用HandlerThread的start()方法。这是什么呢?
答案很简单,因为我们需要一个Looper对象来实例化Handler()。
我们看到在实例化ServiceHandler()的时候,我们首先通过调用thread.getLooper()方法拿到了HandlerThread中的Looper对象。没有看过源码的朋友会不会对为什么thread.getLooper()会拿到一个Looper对象疑惑呢?
-
下面我们看下getLooper()方法源码:
image.png
英语比较好的同学通过方法注释其实已经了解该方法的作用了:该方法返回当前线程的Looper对象。如果这个线程没有启动,又或者不管任何原因isAlive()方法返回false,那么这个方法就会返回null。如果线程已经启动,在Looper对象初始化之前线程会一直处于阻塞状态。
在代码中我们也可以看到,在同步方法中如果isAlive()&&mLooper==null结果为true,那么该线程就会调用wait()方法进入阻塞状态。如果mLooper对象不为null,则直接返回。 -
看完getLooper()源码之后,我们回想一下,mLooper是在什么时候实例化的呢,对,就是在run()方法中,前面定义中讲到,一定要调用start()方法,它会触发系统调用run()方法,而mLooper就是在该方法中实例化的。
请看贴图:
image.png - 下面讲一点题外话,分析源码要看要点,注意力不要放在其他与你目前分析不太相关的代码上,如果实在有兴趣,可以先记下随后了解。如果过于深究每行代码的意思反而会顾此失彼,到最后无法把整个思路串联起来,以上都是个人经验(特别聪明的朋友请忽略我这些话)。
- 我们看到run()方法中主要是调用了Looper.Prepare()来实例化一个Looper对象,然后在同步方法中通过Looper.myLooper()方法来拿到实例化后的Looper对象并赋值给该HandlerThread中的mLooper属性,剩下比较重要的就是调用Looper.loop()方法来轮询消息了。
-
稍微做下扩展,我们来看下Looper.prepare()方法源码
image.png
可以看到prepare()方法中调用了它的重载方法,然后在重载方法中首先通过该线程的sThreadLocal.get()来获取Looper对象,如果对象不为null就会抛出一个”每个线程只能创建一个looper对象“的异常(相信很多人都会遇到过,在我们不清楚源码的时候,确实是会犯一些这样的错误,所以说看源码可以帮我们避开一些坑),如果为null则会new一个Looper对象并通过sThreadLocal的set方法进行保存,以便之后通过get()方法来获取。
接下来我们看下run方法中调用的Looper.myLooper()方法的源码
image.png
代码很简单,就是通过sThreadLocal获取上面我们保存的Looper对象。
啰嗦了一大堆,至此我们的Looper对象拿到了,前面提到getLooper() 方法在Looper对象没有实例化的时候会调用wait()释放锁并进入阻塞状态。我们看下上面贴图run()中的notifyAll()方法,这个方法的作用就是唤醒阻塞状态的线程。
至此我们在IntentService的onCreate()方法中通过HandlerTherad的对象调用getLooper()方法获取到了Looper对象,然后就可以用它来实例化Handler对象了。
3.为什么要使用HandlerThread()?
方便!
假如我们要在子线程中使用Handler如何处理呢?
正常情况下:
- 创建一个Thread,并在run()方法中做如下处理
- Looper.prepare()为当前线程初始化Looper对象
- Looper.myLooper()获取Looper对象
- 使用获取到的Looper对象来实例化Handler
Thread {
Looper.prepare()
val looper = Looper.myLooper()
looper?.let {
mHandler = Handler(it)
Looper.loop()
}
}.start()
如果我们使用HandlerThread则只需要以下代码:
val handlerThread = HandlerThread("handler-thread")
handlerThread.start()
mHandler = Handler(handlerThread.looper)
可以看到,Looper.prepare()和Looper.myLooper()都不需要我们自己处理了而是交给了HandlerThread的run方法来处理。