Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]
思考: 如果让我们来设计一个操作系统, 我们会来怎么设计 ?
一般操作系统都会有一个消息系统, 里面有个死循环, 不断的轮询处理其他各种输入设备输入的消息. 比如键盘的输入, 鼠标的移动等. 这些输入信息最终都会进入操作系统, 然后由操作系统的内部轮训机制挨个处理这些信息.
- 设计一个类, 里面有一个死循环去做循环操作.
- 再用一个类来抽象代表各种输入的信息.
- 这个信息/消息应该还有一个唯一标识符, 用来区分不同的信息/消息
- 信息/消息中有个对象来保存对应的键值对, 方便往信息/消息中存放数据.
- 信息/消息还需要有字段来表明要执行的时间.
- 上面说的这些信息/消息又会组合成一个集合, 常用集合有很多,
ArrayList, LinkedList, Map
. 因为第一点说了使用一个死循环去处理, 那么这个集合最好是线性和排序较好的. 因为输入有先后, 一般都是按照输入的时间先后来构成, 既然这样那就先排除掉Map
, 那么就剩下了ArrayList, LinkedList
. 要知道一个操作系统的事件是非常多的, 也就是说对应的信息/消息很多, 所以这个集合要面临大量的插入操作, 而在插入效率这块,LinkedList
具有明显的优势. 所以这个集合应该是一个链表, 但是链表又分为多种, 因为是线性排序的, 所以只剩下双向链表与单向链表. 又考虑到手机性能的问题, 那就之剩下单向链表了, 因为单向链表在插入和删除上的复杂度明显低于双向链表. - 最后还应该有两个类, 一个负责生产信息/消息, 一个负责消费这些信息/消息. 因为涉及到消费端, 所以在第二点中, 还应该增加一个字段负责指向消费端.
经过这几点的分析, 是不是发现其实和 Handler
机制差不多.
-
Looper
负责轮询 -
Message
代表消息, 内部单向链表.-
what
作为标识符 -
when
代表时间. -
data
数据存放键值对 -
target
指向消费端
-
-
MessageQueue
存放消息集合. -
Handler
负责生产和处理消息.
现在将对 Android 消息机制中的这几个组成部分逐个做出总结.
1. Handler
Handler 是线程间传递消息的即时接口, 分为生产线程和消费线程.
生产线程: 在消息队列中创建, 插入或者移除消息
send *
post *
remove *
消费线程: 处理消息.
handleMessage
每个 Handler 都有一个与之关联的 Looper
和 MessageQueue
, 有两种创建 Handler 的方式 (这里说的两种不是两个构造函数, 而是构造函数的分类. )
- 通过默认的构造方法, 使用当前线程中关联的
Looper
. - 显示的指定使用
Looper
.
如果没有指定 Looper
的 Handler
是无法工作的, 因为它无法将消息放到消息队列中, 同样的, 也无法获取要处理的消息.
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
如果使用的是上面的构造函数, 它会检查当前线程有没有可用的 Looper
对象, 如果没有, 就会抛出运行时异常. 如果正常的话, Handler
就会持有 Looper
中的消息队列对象的引用.
并且, 同一个线程中会有多个 Handler
, 但是他们共享同一个消息队列, 因为他们共享的是同一个 Looper
对象.
2. Message
Message 是容纳任意数据的容器, 生产线程发送消息给 Handler
, Handler
将消息加入到 MessageQueue
中等待执行. Message 提供了 3 种额外的信息, 以供 Handler
和 MessageQueue
处理时使用.
-
what
: 一种标识符,Handler
使用它来区分不同的消息, 从而采用不同的处理方法. -
when
: 告诉消息队列何时处理. -
targe
: 表示是哪一个Handler
应该处理这个消息.
Message 一般是通过以下几种方式来创建的.
public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
Message 通过从消息对象池中获取得到, 方法中提供的参数会放到消息体对应的字段中. Handler
同样可以设置消息的目标为其自身, 这允许我们进行链式调用. 例如
mHandler.obtainMessage(MSG_SHOW_IMAGE, mBitmap).sendToTarget();
消息对象池是一个消息对象的单向链表集合, 它最大长度是 50, 在 Handler
处理完一条消息后, 这条消息就会返回到消息对象池中, 并且重置其所有字段.
当使用 Handler
的 post
方法来执行一个 Runnable
的时候, Handler
就会隐式的为我们创建了一个新的消息, 并且设置 Callback
参数来存储这个 Runnable
.
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
3. MessageQueue
MessageQueue 是一个消息体对象的无界的单向链表集合, 它按照时序将消息插入队列, 最小时间戳的消息将会被优先处理.
MessageQueue 通过 SystemClock.uptimeMillis()
获取系统时间, 维护一个阻塞阈值, 当一个消息体的时间戳低于这个值的时候, 消息就会分发给 Handler
进行处理.
从 MessageQueue 中调用 next
方法循环获取消息的时候, 如果消息为 Barrier
障栅的时候, 该队列中的同步消息全部都会被拦截掉, 而放行所有的异步消息.
从 MessageQueue 中调用 next
方法循环获取消息的时候, 在第一次循环, 并且没有消息或者头部消息的执行时间未到的情况下, 会执行 IdleHandler
. 直到下次调用 MessageQueue.next()
方法. (MessageQueue.next()
方法在 Looper.loop()
死循环中被调用.)
MessageQueue 中的 mPendingIdleHandlers
数组, 初始化的时候, 最小长度为 4.
没有消息或者消息未到执行时间, MessageQueue 将会调用 nativePollOnce
方法进行阻塞, 有新的消息入队会根据情况唤醒. 内部的消息执行时间到了, 也会自动唤醒.
4. Looper
Looper 在 loop()
方法的死循环中调用 MessageQueue.next()
方法获取消息, 然后分发给 Handler
处理. 一旦消息超过阻塞阈, 那么 Looper 就会在下一轮循环中读取到它. Looper 在没有消息分发的时候会进行阻塞状态(其实是 MessageQueue
进行阻塞.), 当有消息时, 会继续轮询.
每个线程只能关联一个 Looper, 给线程附加另外的 Looper 会抛出运行时异常. 通过使用 Looper 的 ThreadLocal
对象可以保证每个线程只关联一个 Looper 对象.
调用 Looper.quit() 方法会立即终止 Looper 的死循环. 并移除消息队列中所有的消息.(延迟消息与非延迟消息).
调用 Looper.quitSafely() 方法将消息队列中所有延迟消息移除, 非延迟消息则派发出去让 Handler
处理.
只有消息队列正在关闭退出的情况下, 在 Looper.loop() 方法死循环中调用 MessageQueue.next()
才会返回 null
.
至此 Handler 消息机制已经分析完毕. 另外还将会有一篇番外的 HandlerThread.