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源码中自行理解。
- Android源码解析--Looper
- Android源码解析--Looper
- Android Looper源码解析
- android Looper 源码解析
- Android-源码解析Handler&Looper
- Android Looper、Handler、Message源码解析
- Android中Looper,MessageQueue,ThreadLocal源码解析
- Looper源码解析
- 深入源码解析Android中的Handler,Message,MessageQueue,Looper
- 深入源码解析Android中的Handler,Message,MessageQueue,Looper
- 深入源码解析Android中的Handler,Message,MessageQueue,Looper
- Android消息机制Handler,MessageQueue,Looper源码解析
- 深入源码解析Android中的Handler,Message,MessageQueue,Looper
- 深入源码解析Android中的Handler,Message,MessageQueue,Looper
- Android消息队列源码解析(Handler、Looper、Message、MessageQueue)
- Android源码:Handler, Looper和MessageQueue实现解析
- Android Looper 源码笔记
- android Looper源码分析
- C++ 循环的嵌套
- Android 7.0 之拍照与图片裁剪适配
- 108-PCF8591 1路AD 1602显示
- 实际app项目中,比较常用、成熟的针对html5的UI框架是哪些,优缺点如何?
- 静态代码块链接数据库,返回链接对象
- android Looper 源码解析
- expdp 遇到 "Streams AQ: enqueue blocked on low memory"
- poj3692(二分图最大独立集)
- linux合并csv
- Java集合的深拷贝
- 截取网络视频的第一帧并显示
- 每日一道算法题——1
- 1053. Path of Equal Weight (30)
- 要过慢生活