概念
Android的UI更新是单线程模型,只能在主线程上操作,在子线程上就要通过使用Handler来进行更新UI的操作
Handler的运行基于MessageQueue(单链表)和Looper, MessageQueue顾名思义是消息队列。但其实是一个单项链表结构来存储信息Message的。而Looper则是不断去读取消息队列MessageQueue中的信息,Handler的工作主要是发送和接收消息,一个线程只有一个Looper
Looper
用于对线程使用消息循环的一个类,在一个线程中调用prepare()方法是为了运行loop, 再调用loop()处理消息直到loop停止
子线程中创建Handler
new Thread(new Runnable() {
public void run() {
//创建消息队列和保存当前的线程
Looper.prepare();
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//在这里处理传入的消息
}
};
//遍历消息队列
Looper.loop();
};
}).start();
为什么子线程更新UI会报错
在子线程中更新UI会报下面错误
Only the original thread that created a view hierarchy can touch its views.
为什么会报这个,在ViewRootImp.java类,中会判断当前线程和主线程
这个类是在onResume之后创建的
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
}
}
在主线程中更新UI,就可以避免多线程操作UI,导致不同步的一些问题
如何处理延迟消息的
-
消息是通过MessageQueen中的enqueueMessage()方法加入消息队列中的,并且它在放入中就进行好排序,链表头的延迟时间小,尾部延迟时间最大
-
Looper.loop()通过MessageQueue中的next()去取消息
-
next()中如果当前链表头部消息是延迟消息,则根据延迟时间进行消息队列会阻塞,不返回给Looper message,知道时间到了,返回给message
-
如果在阻塞中有新的消息插入到链表头部则唤醒线程
-
Looper将新消息交给回调给handler中的handleMessage后,继续调用MessageQueen的next()方法,如果刚刚的延迟消息还是时间未到,则计算时间继续阻塞
Looper为什么不会阻塞主线程
因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
网友评论