从源码看Android消息机制
来源:互联网 发布:x教授 知乎 编辑:程序博客网 时间:2024/05/22 04:41
说到Android的消息机制自然会想到Handler、Message、MessageQueue、Looper这几个类。用一句话概括Android消息机制的过程:通过Handler的sendMessage或post方法发送一个被封装成Message对象的消息,这些消息会被加入到MessageQueue中,由Looper来循环从MessageQueue中取出Message对象,最后回调Handler的handleMessage方法来处理。
一般开发者都已经对这个过程很熟悉了,但是再从源码的角度来看Android消息机制一定会有不一样的收获。下面就从Android源码来看一下Android的消息机制。
一、发送消息
首先消息通过Handler的sendMessage或post方法发出消息。他们都会最终都会调用sendMessageAtTime方法将消息加入到消息队列中,不同的是sendMessage传入的是一个Message对象,而post方法传入的是Runnable对象,需要通过getPostMessage方法进一步封装成Message对象。
//Handler.javapublic final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);}public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0);}private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); //设置Message的callback属性为传进来的Runnable m.callback = r; return m;}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) { //将Message对象的target属性设置为送当前发消息的Handler msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //将消息加入到消息队列中 return queue.enqueueMessage(msg, uptimeMillis);}
二、消息入队列
从上面可以看到通过MessageQueen的enqueueMessage方法将消息加入到了对应的消息队列中了。从这里我们可以看到消息队列是通过单链表来实现的,而其中的mMessages就是这个单链表的头节点了。
在消息加入队列的过程中注意到其中有一个关键的参数when,这个参数就对应了我们使用sendMessageDelayed、postDelayed等类似方法发出消息时所带的时间参数,这表示了该消息最终被回调处理的时间。由此也很容易发现消息队列并不是严格的先进先出,所有的节点都是根据被回调的时间先后排列的,这也就是为什么使用链表来实现消息队列的原因了,因为链表能更快地执行插入和删除操作。
//MessageQueen.javaboolean enqueueMessage(Message msg, long when) { ...... synchronized (this) { ...... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. //如果没有头结点或者当前需要入队的消息比头结点的时间更早,则需要重新构造一个头节点 msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //遍历队列,根据时间顺序找到合适位置插入 for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } ...... } msg.next = p; // invariant: p == prev.next prev.next = msg; } ...... } return true;}
三、获取队列中的消息
在Looper的loop方法会循环从消息队列中获取消息,并进行回调处理。还记得在子线程中接收消息需要额外调用Looper.prepare方法和Looper.loop方法吗?因为在主线程中Android系统已经默默地帮我们把这些步骤都做了。
//ActivityThread.javapublic static void main(String[] args) { ...... Looper.prepareMainLooper(); ...... ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ...... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}
Looper会通过MessageQueue的next方法循环获取消息队列中的第一个节点并回调msg.target的dispatchMessage方法处理消息。而msg.target这个属性的值是在哪里设置的呢?回顾第一部分,Handler在将调用MessageQueen的enqueueMessage将消息入队之前就将Message对象的的target属性设置为发送当前消息的Handler。
//Looper.javapublic static void loop() { final Looper me = myLooper(); ...... final MessageQueue queue = me.mQueue; ...... for (;;) { //从队列中获取消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ...... try { //回调处理消息 msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ...... }}
四、回调处理消息
在回调处理消息的方法中会进行几次判断,显然对于不同的消息可能会有不同的处理方式。首先回顾前面的第一部分就能发现,在使用post(Runnable r)方法发送消息时,会将构造出来的Message的callback属性设置为该Runnable,这种情况下会通过handleCallback(msg)方法执行Runnable中代码。
而mCallback是Handler类的一个属性,这个属性只有在通过下面代码中的两种方式初始化的时候才会被赋值,最后也是回调mCallback的handleMessage方法处理消息。
最后一种情况也就是我们最熟悉的情况了,回调的就是发出该消息的Handler的handleMessage进行处理。
//Handler.javapublic void dispatchMessage(Message msg) { if (msg.callback != null) { //对应通过post方式发送的消息,msg.callback为被传进来的Runnable handleCallback(msg); } else { if (mCallback != null) { //对应初始化当前Handler时使用的是带Callback参数的构造方法 if (mCallback.handleMessage(msg)) { return; } } //对应通过sendMessage方式发送的消息等其他情况 handleMessage(msg); }}private static void handleCallback(Message message) { message.callback.run();}public Handler(Looper looper, Callback callback) { this(looper, callback, false);}public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; //设置mCallback mCallback = callback; mAsynchronous = async;}
到这里,Android消息机制中从消息发出到接收的整个流程就串通起来了,希望能帮助各位更好地理解和使用Android的消息机制。
- Android 从源码看Handler消息机制
- 从源码看Android消息机制
- 从源码看Handler消息机制
- 【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)
- 【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)
- 带你从源码看Android Handler 异步消息处理机制完全解析
- 【Android】从源码角度看Handler机制
- 从源码角度解析Android消息机制
- [Android] 从源码分析 Handler 消息机制
- Android源码 从runOnUiThread聊聊消息机制
- Android 从源码分析Handler消息机制
- 重新从源码的角度看Handler消息通信机制
- Android从源码分析一:Looper,Handler消息机制
- 从源码的角度分析Android消息处理机制
- 从源码的角度理解Android消息处理机制
- android异步消息机制,从源码层面解析(二)
- 从源码一次彻底理解Android的消息机制
- Android 消息处理机制1(从源码分析)
- 题目“计算A+B,输入第一行为一个整数n(1≤n≤10)代表测试的组数。 下面有n组测试数据,每组1行,为2个整数,为A,B。”——代码调试有误
- 容器互联与端口映射
- [蓝桥杯]基础练习 字母图形
- 基础练习 十六进制转八进制
- 编写代码模拟三次密码输入的场景。 最多能输入三次密码,密码正确,提示“登录成功”,密码错误,可以重新输入,最多输入三次。三次均错,则提示退出程序
- 从源码看Android消息机制
- 反思
- Linux基本操作指令
- 创意
- prototype.js 让你更深入的了解javascript的面向对象特性
- 欢迎使用CSDN-markdown编辑器
- NSString 类的内存管理问题
- 网络编程——UDP
- [svc]kvm笔记