1、HandlerThread 使用&源码分析
是什么?
继承 Thread 的类。
有什么用?
它本质是一个带有 Looper 的线程,可以通过 HandlerThread+Handler 实现不同线程之间的消息传递。
有什么优点?
它只有一个优点:就是能够直接创建一个带有 Looper 的线程。也就是说当我们在子线程中使用绑定该线程的 Handler 时,不需要再手动创建 Looper 和开启 Looper 了。
如何使用(下面是一个例子)?
可以看我上传的 github:HandlerThread 使用示例
布局文件:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="downloadImage"
android:text="Download Image" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="downloadAudio"
android:text="Download Audio" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="downloadVideo"
android:text="Download Video" />
</LinearLayout>
<TextView
android:id="@+id/tv_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="30dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="Quit"
android:text="quit" />
</LinearLayout>
MainActivity.java
package com.wlf.test1910031;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextView mShowTv;
private Handler mChildHandler;
private Handler mUiHandler;
private HandlerThread mHandlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mShowTv = findViewById(R.id.tv_show);
mUiHandler = new Handler();
// 1.创建 HandlerThread 的示例对象
mHandlerThread = new HandlerThread("testHandlerThread");
// 2.启动 HandlerThread 子线程
mHandlerThread.start();
// 3.创建绑定子线程 Looper 的 Handler 对象
mChildHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
try {
int what = msg.what;
switch (what) {
case 1:
// 模拟耗时操作
Thread.sleep(1000);
mUiHandler.post(new Runnable() {
@Override
public void run() {
mShowTv.setText("Show Your Image");
}
});
break;
case 2:
Thread.sleep(1000);
mUiHandler.post(new Runnable() {
@Override
public void run() {
mShowTv.setText("Show Your Audio");
}
});
break;
case 3:
Thread.sleep(1000);
mUiHandler.post(new Runnable() {
@Override
public void run() {
mShowTv.setText("Show Your Video");
}
});
default:
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
// 4.发送消息给子线程
public void downloadImage(View view) {
Message firstMessage = Message.obtain();
firstMessage.what = 1;
firstMessage.obj = "DownLoad Image";
mChildHandler.sendMessage(firstMessage);
}
public void downloadAudio(View view) {
Message secondMessage = Message.obtain();
secondMessage.what = 2;
secondMessage.obj = "DownLoad Audio";
mChildHandler.sendMessage(secondMessage);
}
public void downloadVideo(View view) {
Message thirdMessage = Message.obtain();
thirdMessage.what = 3;
thirdMessage.obj = "DownLoad Video";
mChildHandler.sendMessage(thirdMessage);
}
// 5.结束 Looper 消息循环
public void Quit(View view) {
mHandlerThread.quit();
}
}
2、源码分析(基于 API 28)
首先第一步,我们调用 HandlerThread 的构造方法创建了一个 HandlerThread 的对象,这里我们传入的参数是这个 HandlerThread 的名称,用来标记这个线程,看看这个它的构造方法做了什么。
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
可以看到这里调用了父类方法的构造方法创建了一个线程,然后为当前线程设置了默认优先级。HandlerThread 一共有两个构造方法,另一个方法如下所示。
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
这个构造方法同样也会调用父类的方法创建一个线程,然后它能够主动设置一个线程优先级,这个 priority 可以引用 Process 类的常量,优先级最高为-20,最低为19,第一个构造方法的 Process.THREAD_PRIORITY_DEFAULT 为 0,表示默认优先级。优先级高的线程在运行时会具有优先权,但这依赖于线程调度的实现。
接着第二步,我们调用了 handlerThread 的 start(),所以 java虚拟机会调用 HandlerThread 对象的 run() 方法,让我们看看 HandlerThread 中的 run() 方法做了什么。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在 run() 方法中,第一行代码先获取mTid,它是线程的唯一标志。然后调用了 Looper.prepare() 初始化当前线程。接着获取前面初始化过的 Looper,并唤醒其他线程。然后为当前线程设置优先级。然后会调用下面的 onLooperPrepared() 方法,如果我们需要,可以实现这个方法。接着使用 Looper.loop() 开启了消息循环,不断的从消息队列中取出消息。
我们知道 Handler 的使用离不开 Looper,系统自动帮我们在主线程创建了 Looper 并开启了消息循环,所以我们可以自由的在主线程使用 Handler。而若想使用子线程 Handler 就得手动创建 Looper 并开启消息循环,所以 HandlerThread 就帮我们省去了这一步。
然后第三步,创建了一个绑定子线程 Looper 的 Handler,看看 Looper.getLooper() 做了什么。
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;
}
这里首先判断了这个线程是否是存活的,如果线程已经 started 了并且还没有死亡,那么这个线程就是存活的。疑问:为什么在使用 wait() 要在层加 while 循环呢?
public final boolean isAlive() {
return nativePeer != 0;
}
接着判断如果这个线程是存活的并且它的 Looper 还没有被创建,那么使用 wait() 就阻塞这个线程,直到创建了 Looper,使用 notiyAll() 来唤醒线程,然后返回 Looper对象。这也是为什么在 HandlerThread 的 run() 方法中使用了 notiyAll() 的原因。
接着在例子中我们利用这个 Looper 创建了一个绑定子线程的 Handler,也就是 mChildHandler,然后我们可以实现它的 handleMessage 方法,因为它绑定的是子线程 Looper,所以我们可以在这个方法内部使用耗时方法。
第四步就是向子线程发送消息,在 handleMessage 中处理耗时操作,然后通过主线程的 mUiHandler 向主线程发送消息更新 UI。这部分的源码可以参考 Handler 消息通信机制。
最后一步,通过调用 mHandlerThread.quit(),结束该线程的 Looper。
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
可以看到,这里获取了当前线程的 Looper,然后调用了 looper.quit() 去结束这个 Looper。同时它还有一个 quitSafely() 方法。
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
他们最后都会调用 MessageQueue 的 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);
}
}
如上代码所示,调用 quit() 最终会去执行 removeAllMessagesLocked() 方法。
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
可以看到这里把消息链表都清空了。如果调用 quitSafely() 方法,最终会去执行 removeAllFutureMessagesLocked() 方法。
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);
}
}
}
这个方法的大概意思就是先判断当前有没有消息在处理,如果没有的话直接移除消息队列中的所有消息。如果有的话,等这个消息处理完了再去移除消息队列中的消息。
当使用了 quit() 方法时,再使用绑定 HandlerThread 的 Handler 去发送消息时,这个消息就不会进入消息队列了。原因是在使用 quit() 后,mQuitting 就被置为 true 了。
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
}
...
}
可以看到在 enqueueMessage() 方法中,会进行判断,如果这个这个消息循环已经停止了,那么就不会再有消息被加入到消息队列当中去了。
到这里我们的源码分析也结束了。
3、注意事项
最后再使用 HandlerThread 时要注意:
- 在创建了 HandlerThread 对象后,记得使用 HandlerThread.start() 方法去开启消息循环。
- 当我们不需要使用 HandlerThread 时,需要使用它的 quit() 方法退出消息循环。普通 Thread 在 run() 方法结束后,就会自动销毁线程,而 HandlerThread 内部开启了消息循环,所以我们需要手动的去关闭这个循环。
- HandlerThread 只是开启了一个线程,我们使用绑定 HandlerThread 的 Handler 去发送消息时,只是把消息放到了消息队列排队,然后去等待消息的处理。
结尾:HandlerThread 的源码还是比较简单的,但是其中包含了 Handler 消息机制的知识点,如果没有了解过的可以去学习一下,这个也属于必须知道的知识。在学习源码过程中,会将一个个安卓的知识点串联起来,遇到不了解的东西的时候才会去进行了解。最后这篇文章也只是个人看法,如果有不对的地方,欢迎指出。
网友评论