Android(安卓)Java层 Looper 机制
写在前面
本节主讲Android消息循环机制中java 部分:Jave层的Looper,MessageQueue,Handler,涉及到native部分会先做简单解释跳过。后续会在第三节将jave与native 层串联起来。
涉及到的相关java的文件路径:
- Looper.java (android-5.1.0_r3\frameworks\base\core\java\android\os)
- MessageQueue.java (android-5.1.0_r3\frameworks\base\core\java\android\os)
- Message.java (android-5.1.0_r3\frameworks\base\core\java\android\os)
- Handler.java (android-5.1.0_r3\frameworks\base\core\java\android\os)
基本概念
- Looper :负责创建消息队列,并执行消息循环。
- MessageQueue :消息队列,消息以链表形式组织。Message不是直接加入到MessageQueue,而是通过一个与该looper绑定的handler添加进来的。
- Message :消息的格式。
- Handler:派发消息以及处理消息。
创建线程消息队列
主线程消息队列是在应用程序启动时,由framework层帮忙创建的,开发者无需关心。
而如果开发者希望自己创建的子线程也拥有消息循环机制,则需开发者自己去创建。
主线程消息队列的创建
消息队列是与Looper一对一绑定的,在Looper的构造函数中会创建MessageQueue。所以从Looper的构造函数开始。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
Looper的构造函数是private的,因为每个线程中只能有一个looper,所以采用ThreadLocal来保存looper 对象。
// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal sThreadLocal = new ThreadLocal();private static Looper sMainLooper; // guarded by Looper.class
我们知道,系统启动应用程序的时候,就帮我们把主线程中的Looper创建好了,那是哪里创建的呢?
Android 创建应用程序的入口函数是ActivitThread.java 的main() 函数,在main() 函数中调用了Looper的静态方法prepareMainLooper(),将创建了Looper对象以及消息队列。
public static void main(String[] args) { ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}
Looper . prepareMainLooper()
Looper.prepareMainLooper()函数中,会先透过prepare() 创建looper,如果发现之前已经创建过,存在重复创建的话,则会抛出异常,也就是一个线程中只能有一个looper对象的存在。所以Looper中会以ThreadLocal 来保存该对象。即为该变量在每个线程中提供独立的副本。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}
在prepare() 函数中,new 了一个Looper 对象,并将其保存为线程本地变量。
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));}
至此,主线程的Looper对象就创建好了,它是一个ThreadLocal变量, MessageQueue也创建好了。
子线程消息队列的创建
子线程如果需要消息循环机制的话,则需要开发者主动调用Looper.prepare()创建消息队列。
Looper.prepare()
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */public static void prepare() { prepare(true);}
开始消息循环
消息队列创建完之后,会调用Looper.loop() 函数进入消息循环。
此处加上时序图。
Looper.loop()
检查本线程中是否存在Looper对象,不存在则会抛出异常。
final Looper me = myLooper();if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}
进入for死循环,只有当MessageQueue.next()
取得下一个message为null时,则认为messagequeue正在quiting,才会跳出死循环。for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }
获取一个message之后,就将它交给指定的handler去dispatch
msg.target.dispatchMessage(msg);
MessageQueue机制介绍
在介绍next() 函数之前,需要先了解MessageQueue中提供的一些机制。
MessageQueue为存放message的队列
提供添加/移除message的接口
boolean enqueueMessage(Message msg, long when);void removeMessages(Handler h, int what, Object object);
还提供了对同步消息和异步消息不同处理方式
什么是同步消息和异步消息?
那就需要先了解Barrier的概念。
Barrier 意为阻碍,栅栏。顾名思义,当往queue里插入了一个barrier,会阻碍了同步消息的处理,而对异步消息没有影响。只有remove了这个barrier,queue里面的同步消息才能继续被处理。这也是同步消息与异步消息的区别所在。MessageQueue里提供了添加/移除barrier的接口:
int enqueueSyncBarrier(long when);void removeSyncBarrier(int token);
IdleHandler
MessageQueue里维护了一个IdleHandler的ArrayList,那IdleHandler 是用来干什么的呢?
private final ArrayList
mIdleHandlers = new ArrayList (); 先看一下IdleHandler类的注释:
/** * Callback interface for discovering when a thread is going to block * waiting for more messages. */public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle();}
即在空闲的时候,提供一个接口让调用者知道,以便在空闲时候做一些其他操作,比如GC。
使用场景:ActivitThread中有一个GCIdle extends IdleHandler,在queueIdle() 中GC操作。
final class GcIdler implements MessageQueue.IdleHandler { @Override public final boolean queueIdle() { doGcIfNeeded(); return false; }}
MessageQueue . next() 详解
next() 是至关重要的一个函数,Looper.loop()函数每次循环都会调用MessageQueue的next() 函数来获取下一个将要被处理的message。
在next() 函数中需要考虑到Barrier以及IdleHandler存在情况下的处理。
ok,下面来分析源代码。
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } // 这里是利用native的epoll_wait() 来实现消息等待。nextPollTimeoutMillis // 指java层下一个将要被处理的message的时间点。那如果native的pollOnce没有因为 // epoll event 唤醒,则会在nextPollTimeoutMillis时间点返回,以便处理后续的 // message。后续第三节会再深入讲解native部分,在这里只需要将其理解为 // sleep(nextPollTimeoutMillis-now)。 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // msg.target == null 表示该消息为barrier message。 // 如果消息头部是barrier message,则会跳过所有同步消息,寻找异步消息进行处理。 if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // 如果下一个message还没有到处理事件,则将修正nextPollTimeoutMillis // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 否则就返回该message。 // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); return msg; } } else { // 没有消息可处理,将nextPollTimeoutMillis置为-1,表示将会永远阻塞。 // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. // 所有pending message被处理完之后,才开始处理quit message。 if (mQuitting) { dispose(); return null; } // 开始处理存在IdleHandler的情况。当messagequeue为空或者下一个消息 // 还没有到处理时间时,可以认为此时是idle状态。如果存在IdleHandler的话, // 说明有人希望能够在Idle状态下处理一些事情。 // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } // 创建一个size至少>=4的IdleHandler 的数组 if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } // 然后将mIdleHandlers 转换为数组赋给mPendingIdleHandlers。 // 为什么这么做呢?因为后面可能需要边遍历边删除。遍历用mPendingIdleHandlers, // 处理完根据queueIdle返回情况来决定是否删除该IdleHandler。 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // 最后,将nextPollTimeoutMillis置为0。因为在IdleHandler处理的过程中, // 可能已经有一个新的message已经来了,那么当前线程不能进行睡眠等待状态, // 需要立即返回检查是否有新的message是否在pending等待处理。 // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; }}
- 首先是nativePollOnce(mPtr, nextPollTimeoutMillis), 这里是利用native的epoll_wait() 来实现消息等待。
nextPollTimeoutMillis指java层下一个将要被处理的message的时间点。后续第三节会再深入讲解native部分,在这里只需要将其理解为sleep(nextPollTimeoutMillis)。 - 处理Barrier message存在的情况。 msg.target == null 表示该消息为barrier message。如果消息头部是barrier message,则会跳过所有sync message,寻找async message进行处理。
- 如果下一个message还没有到处理时间,则将修正nextPollTimeoutMillis;否则就返回该message。
- 没有msg可处理,nextPollTimeoutMillis为-1,表示将会永远阻塞。
- 所有pending message被处理完之后,才开始处理quit message。
- 处理存在IdleHandler的情况。
当messagequeue为空或者下一个消息还没有到处理时间时,可以认为此时是idle状态。如果存在IdleHandler的话,说明有人希望能够在Idle状态下处理一些事情。
首先获取mIdleHandler的size,然后创建一个size至少>=4的IdleHandler 的数组。然后将mIdleHandlers 转换为数组赋给mPendingIdleHandlers。为什么这么做呢?因为后面可能需要边遍历边删除。遍历用mPendingIdleHandlers,处理完根据queueIdle返回情况来决定是否删除该IdleHandler。 - 最后,将nextPollTimeoutMillis置为0。因为在IdleHandler处理的过程中,可能已经有一个新的message已经来了,所以需要立即看看是否有新的message是否在pending等待处理。
MessageQueue总结:
- 利用barrier提供同步消息和异步消息两种处理方式。
- 利用IdleHandler提供机会让其他人在空闲时间段做事情。
往消息队列发送消息
消息队列创建好了,消息循环也开始了,那可以往消息队列中发送消息了。
Handler的构造过程
public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async;}
需要指定一个Looper,然后从Looper中获取queue(MessageQueue).另外两个参数是:
callback:收到message时,调用callback进行处理。
async:MessageQueue的机制介绍里有提到同步消息与异步消息。默认情况下为false,即为同步消息。
发送消息,其实就是往MessageQueue中add message
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}public final void removeMessages(int what) { mQueue.removeMessages(this, what, null);}
发送消息主要有三种方式:
1. obtain a message and send the message.
sendMessage可能有多种封装形式,但最终都是调用enqueueMessage().
2. post a runnable
runnable 也还是会先封装成message形式。基本上也同send message。
3. runWithScissors(runnable) 发送并执行一个同步的runnable。
- 当在同一个线程时,会直接执行这个runnable,而不需要放到queue里面。
- 当不在同一个线程时,会等这个runnable执行完,才返回,不然会block在这里。也就是Handler中提供的BlockRunnable的作用。
处理消息
在前面的looper.loop()函数中,从messageQueue中获取一个message,然后交给target去dispatchMessage().
msg.target.dispatchMessage(msg);
这里的targer就是handler,
dispatchMessage()
- 先处理runnable的情况
不是runnable的话,交给mCallback的handleMessage, 没有指定mCallback的话,就交给handleMessage处理。继承Handler的子类需要实现handlerMessage接口。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}private static void handleCallback(Message message) { message.callback.run();}
BlockRunnable & runWithScissors
public final boolean runWithScissors(final Runnable r, long timeout) { if (r == null) { throw new IllegalArgumentException("runnable must not be null"); } if (timeout < 0) { throw new IllegalArgumentException("timeout must be non-negative"); } // 当为同一个线程时,直接执行runnable,而不需要加入到消息队列 if (Looper.myLooper() == mLooper) { r.run(); return true; } // new 一个BlockRunnable对象 BlockingRunnable br = new BlockingRunnable(r); return br.postAndWait(this, timeout);}
看一下BlockRunnable的postAndWait() & run()
- post runnable之后,将调用线程变为wait状态
- runnable 执行之后,会通知wait的线程不再wait
public boolean postAndWait(Handler handler, long timeout) { if (!handler.post(this)) { return false; } synchronized (this) { if (timeout > 0) { final long expirationTime = SystemClock.uptimeMillis() + timeout; while (!mDone) { long delay = expirationTime - SystemClock.uptimeMillis(); if (delay <= 0) { return false; // timeout } try { // post runnable 之后,将调用线程变为wait状态 wait(delay); } catch (InterruptedException ex) { } } } else { while (!mDone) { try { // post runnable 之后,将调用线程变为wait状态 wait(); } catch (InterruptedException ex) { } } } } return true; } public void run() { try { mTask.run(); } finally { synchronized (this) { mDone = true; // runnable 执行完之后,会通知wait的线程不再wait notifyAll(); } } }
更多相关文章
- android发现之旅之媒体按键(耳机按键播放暂停键等)处理过程
- :Android(安卓)dispatchTouchEvent
- Android重点
- Android中的Handler的机制与用法详解
- android:configChanges
- Android事件处理的两种模型
- 爱踢门之锤子自由截屏快捷键配置(中)
- Android(安卓)4 游戏高级编程(第2版)
- Android消息机制分析