Android(安卓)Handler 深入学习(2)
2) Looper.loop,进入消息循环
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */public static void loop() { // 这里的 me 即为所在线程中的存储在本地线程局部变量中的 Looper 对象 Looper me = myLooper(); // 取出 looper 中的消息队列,也就是在 prepare() 中创建的队列 MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } long wallStart = 0; long threadStart = 0; ... .. . // 实际的消息分派在这里 // 从第一节的图中可以看到 Message.target 为 Handler msg.target.dispatchMessage(msg); ... ... // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } }} /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */public static Looper myLooper() { return sThreadLocal.get();}
其实从 Looper 的 prepare 和 loop 两个关键方法已经引出了 Handler, MessageQueue, Message,结合图可以简单的说清他们之间的关系:
- Looper 创建了 MQ;
- MQ 使用单链表结构存储了所有 Message;
- 每个 Message 又会关联一个处理此消息的 Handler;
- 而这个 Handler 就是最终用来处理此消息的;
当然,里面也还有很多细节的,我们再从 Looper.loop() 方法中的关键调用 msg.target.dispatchMessage(msg); 的消息分派开始深入了解一下 Handler。
Handler
我结合自己的问题更深入了解 Handler 的:
- 从图中可以看到 Handler 中也有一个 mQueue,这个 mQueue 和 Looper 中创建的队列有什么不同么?
在找这个答案的同时,也正好是了解一下 Handler 的三种构造方式:
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */public 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; mCallback = null;}/** * Constructor associates this handler with the queue for the * current thread and takes a callback interface in which you can handle * messages. */public Handler(Callback callback) { ... ... 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;}/** * Use the provided queue instead of the default one. */public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null;}
代码很清晰,这里的 mQueue 其实就是 Handler 对象所在线程的 Looper 的 MQ,两者是同一个 MQ。在第二个构造方式中,又扯入了一个 mCallback 成员,根据名称应该是 消息的回调,下面在分析 dispatch 时会看到;
- Looper.loop() 中的 queue.next() 的消息来自于哪里,也就是消息怎么入队的?
最简单的方式应该是通过 Looper.getQueue() 得到 MessageQueue 对象,再通过 MessageQueue 的 enqueueMessage() 方法将消息入队。可惜,不行啊,Looper.getQueue() 为 @Hide 的,上层应用无法访问的。可以通过 Handler 的一些辅助方法来访问:
// 将消息加入 MQ 尾部public final boolean sendMessage(Message msg)// 将消息加入 MQ 头部,用于放置优先级较高的消息public final boolean sendMessageAtFrontOfQueue(Message msg)// 移除消息码为 what 的消息public final void removeMessages(int what)// 创建一个消息码为 what 的消息public final Message obtainMessage(int what)// 查看 MQ 中是否有消息码为 what 的消息public final boolean hasMessages(int what)
上面只列出了一部分 Handler 提供的消息访问相关的方法,更多的如下图:
下面为消息入队(sendMessage)的源码过程:
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */public boolean sendMessageAtTime(Message msg, long uptimeMillis){ boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { // 这里将该消息的 target 设置为本 handler msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } ... ... return sent;}
其中,参数 uptimeMillis 为发送消息时的绝对时间,一般应用都是通过调用 sendMessage(Message msg)方法,而该方法会调用 sendMessage(Message msg, long delayMillis),在该方法中又会设置 uptimeMillis 参数,并调用上面的方法实现。
这里的主要动作就是设置 Message 的 target 为调用的 Handler,并将消息入队。
- Handler 的消息分派机制;
public void dispatchMessage(Message msg) { // 如果 Message 本身设置了 callback,则交给 Message 自己的 callback if (msg.callback != null) { handleCallback(msg); } else { // 如果是 Handler 设置了 callback,则交给 handler 的 callback if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 如果都没有设置,则交给 handleMessage handleMessage(msg); }}
可以看到这里的消息分派的机制和流程是:
首先判断被分派的消息本身是否设置了 callback,如果设置了,则交给该 callback,分派结束;
如果 Message 没有设置 callback,则判断 Handler 本身有没有设置 callback,如果设置,则交给 Handler 的 callback,分派结束;
如果 Handler 也没有设置,则调用 Handler.handleMessage(Message),而该 handleMessage 就是应用需要实现的处理消息的地方了。
更多相关文章
- android 一个应用去获取另一个应用assets下面的资源通过框架代码
- Android应用程序键盘(Keyboard)消息处理机制分析(6)
- 【Android】getColor(int id)在API23时过时
- MTK Android(安卓)如何自动挂断电话
- Android(安卓)5.0 API变化
- android 退出全部activity的方法
- getReadableDatabase() 和 getWritableDatabase()
- Android中Log机制
- android ListView本行控件操作本行其它控件的重要方法(绝对原创,本