消息机制是什么
Handler是用于线程之间的消息传递的工具,作用是从一个线程切换到另一个线程。所谓的Android消息机制就是Handler的运行机制。通常我们使用Handler从子线程转到UI线程去更新UI。
Android消息机制概述
Handler的运行需要MessageQueue
和Looper
的支撑。线程内使用ThreadLocal
存储线程数据包括Looper
。Handler使用ThreadLocal
获取每个线程的Looper
。
一些内容概念
MessageQueue
:消息队列,存储消息并以队列形式插入删除数据。
Looper
:一个无限循环,用于接收最新的消息处理
ThreadLocal
:ThreadLocal并不是一个Thread,而是Thread的局部变量。可以视为内部通过一个Map(实际是内部类ThreadLocalMap)存取数据,存取数据只在同一线程有效。
Handler消息机制流程
首先在当前线程创建Handler,同时handler采用当前线程的Looper构建内部消息循环系统(就可以开始等待消息的进入)。(主线程ActivityThread默认有Looper,假设当前线程没有会抛异常。只需要为当前线程创建Looper即可)
然后消息传递线程使用handler的post/send方法将消息加入到MessageQueue队列中。Handler内部的Looper发现新的消息到来,就会处理消息(调用handlerMessage方法)。消息也就被切换到Handler的创建线程中。
以上就是整个的消息循环机制,可能有点绕。但是我总是试图用某种比喻来解释这个过程(以下是我的理解,可能不对):
瞎说大白话
A是一个强大的国家(A线程),而B是一个弱小的国家(B线程)。按道理,B国家需要向A进贡物品(消息Message)。而AB之间有一个很大沟壑,无法直接运输。这时强大的A想了一个办法,由A生产一个可以飞的运输工具(Handler),派到B国家中向A国家驾驶(send方法)。同时A国家这边必须由后勤人员部(ThreadLoacal)派遣自己国家的守城门人员(Looper)日夜守在城门看是否有进贡。当然从B到A这条航线(MessageQueue)会有多个进贡排列进来,守城人员必须一个个装卸(MessageQueue next)和处理(handlerMessage)。
补充
给线程创建Looper
fun thread() {
Thread(Runnable {
Looper.prepare()//给线程添加looper
val handler = Handler()// 如果没有Looper.prepare() 报异常 Can't create handler inside thread that has not called Looper.prepare()
Looper.loop() //开启消息循环
}).start()
}
Android消息机制分析
前面对整个的消息机制进行了概述,对整个消息机制有了了解。而接下来具体分析消息机制中几个成员的具体工作原理(Handler、ThreadLoacal 、MessageQueue 、Looper)。
面试问题
1.为什么不能在子线程更新UI
因为Android的Ui控件不是线程安全的,在多线程更新UI会使UI控件状态不可预期。如果使用加锁机制,会使UI访问机制变得复杂,同时访问的效率也会降低。所以使用单线程模型处理UI控件,而我们只需要转到主线程更新UI即可。
2.主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
当然我们可以这样理解,Android本身就像是一个高速旋转等待处理事件的传送带
3.Handler的sendMessage和Post方法的异同
如下两个方法是等价的
Handler handler=new Handler();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
handler.post(new Runnable() {
@Override
public void run() {
mTest.setText("post");//更新UI
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
Message message = new Message();
message.obj = "handleMessage";
message.what = 0;
myHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
mTest.setText((String) msg.obj);//更新UI
break;
}
}
};
相当于直接将handler.post
里面的代码移到了Handler 的handleMessage
方法内部。
handler.post
内部的run方法已经是handler looper
所在的线程了,所以可以直接更新UI 这时可以将线程获取的值发送到UI去更新界面。而handler.post本质上还是使用了send方法
结果就是handler.post
更加清爽和简洁。但是如果需要控制流程,就只能使用sendMessage
方法
网友评论