Android中的异步消息解决机制
这也是
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<Looper>保存在线程的私有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_Handler_MessageQueue.png
即:
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升级都是建立在这个模型之上的
用下面这个图总结一下整个运行机制:
Android消息解决模型.png
多线程中的消息解决机制的应用
举一个最典型的例子: 在下载线程中完成了下载,通知主线程升级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,而后互相发送消息
欢迎关注我的Android进阶计划看更多干货
微信公众号:
微信公众号.jpeg
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android中的异步消息解决机制