android Looper 源码解析

来源:互联网 发布:ubuntu server wifi 编辑:程序博客网 时间:2024/06/14 00:43

android Looper 官网解释:

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare in the thread that is to run the loop, and then loop to have it process messages until the loop is stopped.

翻译成中文:

Looper 类,用于运行线程的消息循环。线程默认情况下没有与它们相关联的消息循环;要创建一个线程,需要在线程中准备运行循环,然后循环执行进程消息,直到循环停止。

This is a typical example of the implementation of a Looper thread,using the separation of prepare and loop to create an initial Handler to communicate with the Looper.

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的主要使用场景 —非主线程需要Handler发送消息。

在上述示例中提到了Looper中两个重要的方法 perpare()、loop()

 /** 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);    }    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));    }

prepare()方法中使用了ThreadLocal对象,而该对象是一个线程内部的数据存储类,具体的内部原理可以参考:

http://blog.csdn.net/liuluchao0543/article/details/52489547

同时在该方法中创建出了一个Looper对象:

 private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

在Looper的构造方法中创建出一个新的MessageQueue对象,该对象的内部原理可以参考:

xxxxxxx

至此可以看出当Looper调用prepare()方法时,实质上只是创建出一个MessageQueue队列。

示例demo中在调用prepare()方法之后,紧接着调用了 Looper.loop()方法,该方法在Looper.java 中的示例源码:

  /**     * Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the loop.     */    public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final 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();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // 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.recycleUnchecked();        }    }

loop方法主要是:在线程中运行消息队列,并从消息队列中获取消息Message:对于Message的内部实现原理可以参考:

xxxxx

下面来具体分析loop方法的具体实现。

final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }public static Looper myLooper() {        return sThreadLocal.get();    }

在调用loop方法时,会首先判断当前线程中是否已存在looper对象,如果当前线程中looper对象为null 则抛出异常:No Looper; Looper.prepare() wasn’t called on this thread.

 final MessageQueue queue = me.mQueue;

之后通过looper对象 获取MessageQueue对象。

Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();

以上两行代码在Binder.java 中可以看出实质是通过jni调用了底层处理,具体处理暂时无法获取,通过注释

 /**     * Reset the identity of the incoming IPC on the current thread.  This can     * be useful if, while handling an incoming call, you will be calling     * on interfaces of other objects that may be local to your process and     * need to do permission checks on the calls coming into them (so they     * will check the permission of your own local process, and not whatever     * process originally called you).     *     * @return Returns an opaque token that can be used to restore the     * original calling identity by passing it to     * {@link #restoreCallingIdentity(long)}.     *     * @see #getCallingPid()     * @see #getCallingUid()     * @see #restoreCallingIdentity(long)     */    public static final native long clearCallingIdentity();

可以看出clearCallingIdentity() 方法的目的是:确保此线程的标识是本地进程的标识,并跟踪此线程本地标识。

同时在loop方法的for(;;) 循环中看到

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);            }

每次循环 都会检查 此线程的本地标识是否一致,如果不一致 则会执行Log打印否则 不会执行。

紧接着来细看loop中的for(;;)循环具体做了什么?

  for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // 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.recycleUnchecked();        }

for (;;) 代表一个无休止循环。如果循环中没有终止,那么该循环将会一直循环下去。
在该循环中 只需要关注这几行代码即可,而像另外的几行代码,在if条件成立的情况下是一个logging日志的打印。

 Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }  .....   msg.target.dispatchMessage(msg);   ....       msg.recycleUnchecked();

上述几行代码可以看到 通过MessageQueue的next()方法获取到队列中的Message消息,如果消息为null 则跳出循环条件
如果不为null 那么直接调用 msg.target = Handler 对象的dispatchMessage(msg) 方法
最后 直接调用Message对象的recycleUnchecked()方法,而该方法主要是对消息进行回收。

至此可以看出loop方法的主要作用是一个无限for循环,在该循环中如果MessageQueue中消息不为null,那么将会从该队列中一直去取Message,并将该Message通过参数方法传递给Handler。

下面在介绍一下Looper.java中其它的一些方法:

 public static MessageQueue myQueue() {        return myLooper().mQueue;    }

获取当前管理的MessageQueue对象

 /**     * Initialize the current thread as a looper, marking it as an     * application's main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }

为当前线程初始化一个looper对象,并将该looper对象标记为application main looper

 /** Returns the application's main looper, which lives in the main thread of the application.     */ public static Looper getMainLooper() {        synchronized (Looper.class) {            return sMainLooper;        }    }

获取主线程的Looper对象

Looper是通过调用loop方法驱动着消息循环的进行: 从MessageQueue中阻塞式地取出一个消息,然后让Handler处理该消息,周而复始,loop方法是个死循环方法。

那如何终止消息循环呢?我们可以调用Looper的quit方法或quitSafely方法,二者稍有不同

 /**     * Quits the looper.     * <p>     * Causes the {@link #loop} method to terminate without processing any     * more messages in the message queue.     * </p><p>     * Any attempt to post messages to the queue after the looper is asked to quit will fail.     * For example, the {@link Handler#sendMessage(Message)} method will return false.     * </p><p class="note">     * Using this method may be unsafe because some messages may not be delivered     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure     * that all pending work is completed in an orderly manner.     * </p>     *     * @see #quitSafely     */ public void quit() {        mQueue.quit(false);    }

当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

 /**     * Quits the looper safely.     * <p>     * Causes the {@link #loop} method to terminate as soon as all remaining messages     * in the message queue that are already due to be delivered have been handled.     * However pending delayed messages with due times in the future will not be     * delivered before the loop terminates.     * </p><p>     * Any attempt to post messages to the queue after the looper is asked to quit will fail.     * For example, the {@link Handler#sendMessage(Message)} method will return false.     * </p>     */    public void quitSafely() {        mQueue.quit(true);    }

当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

注:无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。

需要注意的是Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。

至此Looper.java的源码解析已经解析完成,如果不明白的地方读者可留言,或者从android源码中自行理解。

0 0
原创粉丝点击