目录
一、HandlerThread简介
1. HandlerThread产生背景
开启Thread子线程进行耗时操作
多次创建和销毁线程是很耗系统资源的。
(为什么呢,因为java的线程是依托操作系统,线程的创建和销毁以及频繁切换都涉及到用户态和内核态的切换,以及线程上下文的保存和恢复,所以是比较耗资源的。)
既然已经有Handler可以实现线程间通信,为什么又设计了HandlerThread?
HandlerThread通过字面意思我们可以看到,它是Handler+Thread,那么我们猜测它应该实现了Handler和Thread功能,到底是不是呢,我们向下看。
首先,HandlerThread继承了Thread类,也就是说HandlerThread可以创建一个新的线程。
其次,HandlerThread内封装了Handler类,并自动创建了Looper和MessageQueue,也就是说我们使用HandlerThread线程时,不需要手动创建Looper。
优点:
之前我们使用Handler时,必须自己创建线程,并且自己创建Looper等相关的功能,而HandlerThread则提供了一种方便的方式,相当于内部已经集成了这些功能,不需要我们再手动创建Looper。
在Android开发中,通常在主线程(也称为UI线程)中执行耗时操作会导致界面卡顿甚至ANR(Application Not Responding)错误。为了避免这种情况,可以使用HandlerThread。
HandlerThread是一个带有Looper的线程,可以用来处理消息队列中的消息。通过HandlerThread,可以在后台线程执行耗时操作,然后通过Handler将结果发送回主线程更新UI,从而提高应用的性能和流畅度。
使用HandlerThread的好处包括:
- 避免在主线程执行耗时操作导致界面卡顿
- 可以轻松地在后台线程执行网络请求、数据库操作等耗时任务
- 可以方便地进行线程间的通信和数据传递
- 可以更好地控制线程的生命周期和执行顺序
总之,使用HandlerThread可以帮助提高应用的性能和用户体验,是Android开发中常用的线程处理方式之一。
2. HandlerThread 的特点
本质上就是封装了Handler的线程
- HandlerThread 本质是一个线程类,它继承自Thread
- HandlerThread 有自己的内部looper对象,可以进行loop循环
- 通过HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
- 不会阻塞,减少了对性能的消耗,缺点是不能同时进行多任务的处理,需要等待进行,效率低。
这里的不会阻塞,指的是非UI线程,又想使用消息机制的那些操作。通过HandlerThread来执行是最合适的。
5.与线程池并发不同,HandlerThread是一个串行队列,HandlerThread只有一个线程。
HandlerThread是一个轻量级的异步类,可以实现多线程,并且可以实现线程间的通信(HandlerThread主要应用是实现主线程到子线程的通信,子线程到主线程通信可以通过Handler机制)。
HandlerThread继承自Thread,内部实现了初始化了Looper,并创建了消息队列,接着调用了Looper.loop()开启了消息循环,这样HandlerThread就可以处理通过Handler传递过来的Message了,因为HandlerThread中的run方法是无限循环,当有消息过来时处理消息,没有消息时就会阻塞。当明确不需要HandlerThread时,可以调用quit或者quitSafely (API 18以上使用)来尝试终止线程。
二、HandlerThread用法
1、通过callback创建
1.创建HandlerThread
HandlerThread handlerThread = new HandlerThread("handlerThread_name");
//2.必须先开启线程
handlerThread.start();
/**
* 3.设置callback
* 该callback运行于子线程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子线程中进行相应的网络请求
//通知主线程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
//4.子线程Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
2、通过Looper创建
使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。
mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法
//new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,
//进而绑定到了线程mHandlerThread
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//对信息的相关处理操作
//在子线程mHandlerThread中运行
super.handleMessage(msg);
}
};
//4、创建消息并发送消息
//在主线程中
Message msg = Message.obtain();
//主线程向子线程mHandlerThread发送消息通信
mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程
//即停止了线程的消息循环
mHandlerThread.quit();
通过使用步骤我们可以看到,HandlerThread实现的功能主要就是主线程向子线程通信,另外可以在使用Handler实现子线程到主线程的通信,进而就可以实现主线程到子线程间的双向通信。
3、举例
handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
//在主线程初始化,传入的是HandlerThread中的Looper
workHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//Handler是在HandlerThread所在的子线程线程中处理Message
switch (msg.what) {
case MSG_UPDATE:
if (isStop) return;
try {
String result = String.valueOf(new Random().nextInt(9000) + 1000);
//通过uiHandler对象传送消息到主线程,更新UI
sendMsg(UI_UPDATE, result, uiHandler);
//延迟2秒
Thread.sleep(2000);
//循环发送消息
workHandler.sendEmptyMessage(MSG_UPDATE);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
};
首先,初始化HandlerThread并通过handlerThread.getLooper()关联一个在UI线程初始化的workHandler,这样就可以通过workHandler在主线程中向HandlerThread中发送消息了,这里要注意一下,workHandler 中的handleMessage()方法是在HandlerThread子线程中执行的,为什么会在子线程中执行呢?
因为在初始化workHandler 时传入的是HandlerThread的Looper,而workHandler 是把消息发送到HandlerThread中去,HandlerThread在执行Looper.loop()方法后会循环取出消息并处理消息,所以workHandler 中的handleMessage()方法是在HandlerThread子线程中执行的,那么处理完消息后怎么更新到UI线程呢?
//在主线程初始化,传入的是主线程中的Looper
uiHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
//在主线程中处理Message
switch (msg.what) {
case UI_UPDATE:
//更新验证码
String result = (String) msg.obj;
tv_random.setText(result);
break;
}
}
};
我们在主线程中初始化了另一个uiHandler 并传入了主线程的Looper,所以此时最后处理消息的回调方法handleMessage()是在主线程中执行的,当HandlerThread处理完逻辑后,通过uiHandler把结果发到主线程中,就可以愉快地来更新UI了,最后别忘了关闭线程:
@Override
protected void onDestroy() {
if (SDK_INT >= 18) {
handlerThread.quitSafely();
} else {
handlerThread.quit();
}
super.onDestroy();
}
三、HandlerThread生命周期管理
使用HandlerThread完成任务后,要记得调用quit方法退出线程。避免出现内存泄漏问题!!!
四、HandlerThread API及源码分析
run方法
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {//同步代码块,确保同一时刻只有一个线程可以访问mLooper
mLooper = Looper.myLooper();
notifyAll();//通知当前等待线程,主要是getLooper()方法里的wait方法
}
Process.setThreadPriority(mPriority);//设定线程优先级
onLooperPrepared();//空方法,留个用户自己重写
Looper.loop();//初始化数据接收,开启循环。
mTid = -1;
}
getLooper()方法
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {//同步代码块,如果looper没有创建成功,就一直阻塞,直到looper创建完毕。
while (isAlive() && mLooper == null) {
try {
//这个是没有设置TimeOut的Object:wait方法,无限期等待,必须是其他线程调用notify或者notifyAll才能唤醒。
//当run方法的mLooper对象创建完毕时,就会调用notifyAll方法唤醒。
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
quit和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;
}
quitsafely比quit更安全,各自的实现原理如下。
//Looper.java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
//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();
} 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);
}
}
}
推荐文章
HandlerThread原理、使用实例、源码详细解析-优快云博客
https://zhuanlan.zhihu.com/p/424513321