Handler机制工作原理
定义
是Android的一套消息传递机制
将工作线程中需更新UI
的操作信息 传递到 UI
主线程,从而实现 工作线程对UI
的更新处理,最终实现异步消息处理
ABOUT
主线程 | 处理与UI有关的操作 |
---|---|
子线程 | 处理一些耗时的操作(人为手动开启) |
Message | 是线程之前通讯的数据单元,存储需要传递的消息 |
Handler | 添加Message到Message Queue;处理Looper的Message。是线程消息的主要处理者。 |
Looper | 消息循环的核心 |
Message Queue | 是一种数据结构,存储Message |
UI操作是线程安全的,仅允许UI线程(主线程)操作UI,为了满足:多个线程可并发操作UI以及保证线程安全,使用Handler通知主线程更新UI。
流程
-
异步通信准备
在主线程创建Looper,Message Queue(可以理解为从属于Looper),以及Handler对象
-
消息发送
Handler将Message发送到Message Queue
-
消息循环
Looper取出Message Queue中的消息
Looper将取出的Message发给处理者Handler
-
消息处理
Handler接收并处理Message,并进行UI操作
- 1个线程
(Thread)
只能绑定 1个循环器(Looper)
,但可以有多个处理者(Handler)
- 1个循环器
(Looper)
可绑定多个处理者(Handler)
- 1个处理者
(Handler)
只能绑定1个1个循环器(Looper)
一. 创建Looper对象
-
主线程创建的时候自动创建Looper,无需手动创建
-
Looper的构造方法私有,仅在
prepare()
中使用,如果threadLocal能获取到值意味着已经创建过,会抛出异常,保证了一个线程仅能拥有一个Looper。 -
prepare()
方法的参数中有一个boolean,表示是否可以退出循环,主线程不可以,子线程可以。 -
Looper的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
创建Looper对象时,同时也绑定了一个Message Queue以及当前线程。
二. Looper开始工作
调用loop()
方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
。。。
for (;;) {
Message msg = queue.next(); // might block
。。。
}
}
-
首先获取绑定的Message Queue
-
for(;;)
开启死循环 -
queue.next()
获取Message -
处理消息
循环虽然是死循环,但有方法可以退出:
quit()
:立即退出
quitSafely()
:先打标记安全退出,即处理完Message之后,再推出
三.Message Queue读取数据
-
nextPollTimeoutMillis
是很重要的参数,正常循环时,此参数为0。当有延迟消息,消息没准备好时,则会设置一个延迟时间,当Message为空,则设置为-1 -
nextPollTimeoutMillis
为-1时,消息队列陷入阻塞状态 -
为了保证线程安全,在这个线程中要锁住Message Queue对象
使用
Handler.sendMessage()
以下代码采用自定义一个Handler类继承自Handler,也可以选择匿名内部类
//新建一个自定义Handler对象
MyHandler handler=new MyHandler();
//新开工作线程,handler将消息发送,并开启线程
new Thread(new Runnable() {
@Override
public void run() {
Message msg=Message.obtain();
msg.what=1;
msg.obj="nihao";
handler.sendMessage(msg);
}
}).start();
//自定义一个Handler,并在handlerMessage方法中完成主线程的UI操作
private class MyHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what==1){
}
}
}
Handler.post()
此种方式是在新线程使用handler提交UI操作
final Handler handler=new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
binding.tv.setText("niniin");
}
});
}
}).start();
以上两种方式都是在主线程使用Handler的,在子线程,Looper不会自动创建,需要手动创建并且loop
代码展示:(以post的方式)
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler1=new Handler();
handler1.post(new Runnable() {
@Override
public void run() {
//
}
});
Looper.loop();
}
}).start();
总结
post方法和handleMessage方法的不同在于,post的runnable会直接在callback中调用run方法执行,而sendMessage方法要用户主动重写mCallback或者handleMessage方法来处理。
其他
Looper的死循环
Looper不会一直消耗系统资源,当Looper的MessageQueue中没有消息时,或者定时消息没到执行时间时,当前持有Looper的线程就会进入阻塞状态。
当有消息入队时则会唤醒线程(重点:消息入队按照delay时间,时间短先入队,时间长后入队,时间相同则看入队时间先后)
Handler是如何引起内存泄漏的以及解决
当Handler是非静态内部类或匿名Handler内部类时,会持有外部应用,在Activity销毁时,由于Handler可能有未执行完/正在执行的Message。导致Handler持有Activity的引用。进而导致GC无法回收Activity
Handler
的生命周期 > 外部类的生命周期
以上都会导致内存泄漏
解决:
Activity销毁时,清空Handler中,未执行或正在执行的Callback以及Message。
静态内部类+弱引用
Handler 是如何能够线程切换的
其实并没有线程切换,线程的切换执行关键也不在于Handler所在的线程而是在于Looper是在哪一个线程创建的。
Handler
的生命周期 > 外部类的生命周期
以上都会导致内存泄漏
解决:
Activity销毁时,清空Handler中,未执行或正在执行的Callback以及Message。
静态内部类+弱引用
Handler 是如何能够线程切换的
其实并没有线程切换,线程的切换执行关键也不在于Handler所在的线程而是在于Looper是在哪一个线程创建的。