Handler异步消息处理机制是安卓最常见内容之一,
HandlerThread是什么?⭐⭐⭐⭐⭐
HandlerThread原理和使用场景?⭐⭐⭐⭐
目录
1、HandlerThread是什么?
2、HandlerThread怎么使用
3、源码分析
4、总结
1、HandlerThread是什么?
在安卓开发中,如果需要执行耗时操作,则可以开启子线程来完成,然而手动创建销毁线程又麻烦又消耗系统性能,因此可以使用线程池来完成。如果还需要在线程中使用Handler异步消息机制,或者需要实现子线程和子线程之间的通讯(Handler是主线程和子线程之间的通讯),那么就可以用HandlerThreaad。
HandlerThread是Google封装好的一个类,它的内部有自己的Looper对象,可以进行Loop轮询,用于执行多个耗时操作,而不需要多次开启线程,本质是使用Handler和Looper实现的。
2、HandlerThread怎么使用
如果我们需要使用HandlerThread来读取一个大文件的内容,可以这么写:
public class Xuruictivity extends AppCompatActivity {
private static final int MSG_READ_INFO = 100;
Handler mMainHandler = new Handler();
private Handler mThreadHandler;
private HandlerThread mHandlerThread;
private boolean isFinish = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xurui);
initHandlerThread(); //1
startHandlerThread();
}
private void initHandlerThread() {
mHandlerThread = new HandlerThread("Xurui"); //2
mHandlerThread.start(); //3
mThreadHandler = new Handler(mHandlerThread.getLooper()) { //4
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_READ_INFO: {
readFileInfo();
// 如果没有读完,则继续
if (!isFinish) {
mThreadHandler.sendEmptyMessage(MSG_READ_INFO);
}
}
break;
default:
break;
}
}
};
}
/**
* 模拟读取文件
*/
private void readFileInfo() {
try {
//模拟耗时
Thread.sleep(1200);
mMainHandler.post(new Runnable() {
@Override
public void run() {
// 读取大文件内容
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void startHandlerThread() {
mThreadHandler.sendEmptyMessage(MSG_READ_INFO);
}
@Override
protected void onPause() {
super.onPause();
mThreadHandler.removeMessages(MSG_READ_INFO); //5
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();//6
mMainHandler.removeCallbacksAndMessages(null); //7
}
}
使用mHandlerThread的looper创建的mThreadHandler,对应注释4,里面的handleMessage是可以进行耗时操作的,因为它是执行在mHandlerThread所在的子线程。因此其优点是不会阻塞主线程,但是多任务时也需要有序执行,导致执行效率低。因此HandlerThread比较适合耗时不且不会产生较大的阻塞,比如读取文件,操作数据库等,至于网络IO操作这种可能有较大阻塞等,HandlerThread并不适合。
上面代码最关键的步骤都在注释1的函数里。我们来看看HandlerThread的使用方法:
创建HandlerThread实例:对应注释2,其中“xurui”是子线程的名字,可以随意取;
执行start函数来启动HandlerThread线程:对应注释3;
将HandlerThread和Handler绑定:对应注释4;
然后执行startHandlerThread()就行了,最后记得退出HandlerThread,对应注释6。
为何是这样的步骤,那就进入激动人心的源码分析吧!
3、源码分析
HandlerThread的源码仅仅100+行,因此直接copy完整的源码,不做任何删减(下面源码是android9.0.0的源码)。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) { //1
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) { //2
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(); //3
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //4
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop(); //5
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() { //6
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(); //7
} 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;
}
}
构造方法
关键看看注释2的构造函数,其中name是该线程的名字,priority是线程的优先级,在此默认是THREAD_PRIORITY_DEFAULT,这些宏是在 Process 中定义的,值越小,进程优先级越高,主线程的优先级也是THREAD_PRIORITY_DEFAULT。
run方法
在该方法里,执行了很经典的函数,也就是注释3的 Looper.prepare(),详情见本系列Handler小节,该方法主要是创建Looper实例,只有创建了Looper实例,才能将HandlerThread和handler绑定在一起。因此需要先执行start()函数,执行后,线程会交给虚拟机进行调度并自动调用run()方法。
注释5相信看过Handler小节的同学也很清楚了,看看注释4,为何这里会有notifyAll()方法呢?而且还使用了synchronized。
HandlerThread和handler绑定
这里关键是看看getLooper()函数,对应注释6,这里也有synchronized,同时还有wait()方法,众所周知,wait()和notifyAll()是搭配使用来实现线程同步的。这里为了获取到mLooper对象,需要进行线程同步。因为我们希望getLooper()函数只有在线程创建成功且Looper实例也创建成功时才返回,因此在注释7开始阻塞,直到注释4已经创建完Looper实例时才唤醒。
quit和quitSafe方法
最后看看如何退出线程。当调用quit和quitSafe方法就可以退出Looper消息循环并退出线程,从名字上就可以知道后者是“安全退出“。
跟踪这两个方法,会跑到MessageQueue.java里的:
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(); //8
} else {
removeAllMessagesLocked(); //9
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
如果安全则调用注释8,不安全调用注释9.继续看源码。removeAllMessagesLocked()只是遍历链表并把所有消息移除掉,最后将mMessages设置为null。而removeAllFutureMessagesLocked()会根据when变量,对应下面注释10,如果当前没有处理消息则同样移除所有消息,否则等待消息处理完毕后再退出循环。
因此这里所谓的安全或者不安全是因为quit()方法无论当前是否有处理消息,都强制移除所有回调,所以不安全。
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) { //10
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);
}
}
}
4、 总结
最后做个总结:
HandlerThread 是内部会创建Looper的线程,因此只能作为子线程使用;
HandlerThread 需要配合 Handler 使用,HandlerThread 的耗时操作在handleMessage()中执行,因为此时执行的是在HandlerThread所在的子线程执行的,但不能像普通线程一样在run()方法中进行,因为HanderThread的run()方法已经被写死。
HandlerThread和Handler绑定之前,必须先调用 mHandlerThread.start() 让 run() 方法跑起来.