这也是
Android
中老生常谈的一个话题了,它本身并不是很复杂,可是面试官比较喜欢问。本文就从源码再简单的理一下这个机制。也可以说是理一下Handler
、Looper
、MessageQueue
之间的关系。
单线程中的消息处理机制的实现
首先我们以Looper.java
源码中给出的一个例子来分析一下在单线程中如何使用这个机制:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Looper.prepare()
上面只涉及到Handler
和Looper
,MessageQueue
呢?我们来看一下Looper.prepare()
:
private static void prepare(boolean quitAllowed) {
...
sThreadLocal.set(new Looper(quitAllowed));
}
很简单,即new Looper
,然后把它放到ThreadLocal<Looper>
中(ThreadLocal保存在线程的私有map中)。继续看一下Looper的构造方法
:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
...
}
即,在这里MessageQueue
作为Looper
的成员变量被初始化了。所以 一个Looper对应一个MessageQueue 。 ok,到这里Looper.prepare()
所涉及的逻辑已经浏览完毕,继续看一下new Handler()
:
new Handler()
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
Handler
会持有一个Looper
, 那我们看一下这个Looper
来自于哪里: Looper.myLooper()
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
即会从当前线程的私有map中取出ThreadLocal<Looper>
。所以Handler
默认持有当前线程的Looper
的引用。如果当前线程没有Looper
,那么Handler
就会构造失败,抛出异常。其实可以在构造Handler
时指定一个Looper
,下面会讲到这个。
在持有当前线程的Looper
的引用同时,Handler
在构造时也会获取Looper
的成员变量MessageQueue
,并持有它。 如果你在一个线程中同时new多个Handler
的话,那他们的关系如下图所示:
即:
Looper
和MessageQueue
存放在当前线程的ThreadLocal
中Handler
持有当前线程的Looper
和MessageQueue
。
Looper.loop()
这个方法可以说是核心了:
public static void loop( ) {
final Looper me = myLooper();
//...
final MessageQueue queue = me.mQueue;
//...
for (;;) {
Message msg = queue.next(); // might block
//…
msg.target.dispatchMessage(msg);
//...
}
}
即Looper
不断检查MessageQueue
中是否有消息Message
,并调用msg.target.dispatchMessage(msg)
处理这个消息。 那msg.target
是什么呢?其实它是Handler
的引用。
msg.target.dispatchMessage(msg)
会导致Handler.handleMessage()
的调用,其实到这里单线程中的消息处理机制模型
已经有了一个大致的轮廓了,接下来就需要弄清楚
msg.target
是在哪里赋值的?- 消息是如何插入到
MessageQueue
中的?
发送一个消息
以Handler
发送一个最简单的消息为例:
handler.sendEmptyMessage(0)
这个方法最终调用到的核心逻辑是:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
即Handler
会把Message
的target
设置为自己,并把消息放入到当前线程的MessageQueue
中。
这样Looper.loop()
方法中就可以从MessageQueue
中取出消息,并把消息发送到msg.target(Handler)
去处理,其实msg.target.dispatchMessage(msg)
会调用Handler.handleMessage()
方法。
到这里就完成了整个消息处理模型
的分析。其实 整个Android主线程的UI更新
都是建立在这个模型之上的
用下面这个图总结一下整个运行机制:
多线程中的消息处理机制的应用
举一个最典型的例子: 在下载线程中完成了下载,通知主线程更新UI。怎么做呢?
明白了上面Handler/MessageQueue/Looper
的关系后,我们只需要往主线程的MessageQueue
中发送一个更新UI的Message
即可,那怎么往主线程发消息呢?
指定Handler所依附的Looper
对,只需要在构造Hander时把主线程的Looper传递给它即可:
downLoadHandler = Handler(Looper.getMainLooper())
这样downLoadHandler.sendEmptyMessage(2)
就会发送到主线程的MessageQueue
中。handler.handleMessage()
也将会在主线程中回调。
其实更简单的是在主线程中保存一个Handler
成员变量,子线程直接拿这个Handler
发消息即可。
但在多线程使用Handler
时因为涉及到线程切换和异步,要注意内存泄漏和对象可用性检查。比如在更新UI时Activity
是finish
状态,这时候就需要你的update
是否可以继续执行等。
线程通信
运用这个模型我们可以很方便的进行线程通信:
- 需要通信的两个线程都建立
Looper
- 双方分别持有对方的handler,然后互相发送消息
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20139,转载请注明出处。
评论0