Android Looper机制简介
来源:互联网 发布:linux ping 编辑:程序博客网 时间:2024/04/30 14:44
一、主线程和Looper
我们知道android中可以使用Handler向主线程发送消息,来实现线程间的异步通信,AsyncTask内部其实也是使用Handler实现的。主线程之所以可以接收Handler消息,是因为主线程在启动时,已经创建了Looper对象。
/*** ActivityThread.java ***/public static void main(String[] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop();}
二、工作线程使用Looper
而我们如果直接创建一个线程,是无法接收Handler的消息的,需要为该线程创建一个Looper对象才可以。
创建带有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创建,和上面的过程是一样一样的。
下面根据这个过程,简单介绍一下Android的Looper机制。
三、Looper机制简介
Android应用程序是通过消息来驱动的,每一个拥有Looper的线程(如主线程),都有一个消息队列,其他线程向消息队列里放入消息,Looper线程不断循环地从消息队列里取出消息并处理。没有消息可处理时,Looper线程就进入阻塞状态,直到有新的消息需要处理时被唤醒。
四、代码位置
基于Android6.0的代码
涉及到的类的代码位置
frameworks/base/core/java/android/os/Handler.javaframeworks/base/core/java/android/os/Message.javaframeworks/base/core/java/android/os/MessageQueue.javaframeworks/base/core/java/android/os/Looper.javaframeworks/base/core/jni/android_os_MessageQueue.cppsystem/core/libutils/Looper.cpp
五、Looper.java简化
package android.os;public final class Looper { //为每一个线程维护一个Looper static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //主线程Looper,每个进程只有一个 private static Looper sMainLooper; //Looper的消息队列 final MessageQueue mQueue; //Looper所在的线程 final Thread mThread; public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //同一个线程不能重复创建Looper if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } //创建主线程Looper,由系统调用 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } /** * Returns the application's main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } /** * 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; for (;;) { 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(); } } /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** * Return the {@link MessageQueue} object associated with the current * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } //直接退出,回收消息队列里所有的消息 public void quit() { mQueue.quit(false); } //处理完当前的消息,回收消息队列里要在将来某个时刻处理的消息 public void quitSafely() { mQueue.quit(true); } /** * Gets the Thread associated with this Looper. * * @return The looper's thread. */ public @NonNull Thread getThread() { return mThread; } /** * Gets this looper's message queue. * * @return The looper's message queue. */ public @NonNull MessageQueue getQueue() { return mQueue; }}
六、Looper的初始化流程
1.Looper.prepare()
调用Looper.prepare()会new一个Looper对象,Looper的构造函数中,又会new一个消息队列MessageQueue。
/*** MessageQueue.java ***/MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit();}通过JNI机制,MessageQueue调用nativeInit()函数,在JNI层创建一个NativeMessageQueue对象,并将该对象的句柄保存在mPtr变量中。代码如下
/*** android_os_MessageQueue.cpp ***/static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast<jlong>(nativeMessageQueue);}NativeMessageQueue初始化时又会创建一个native层的Looper对象。
/*** android_os_MessageQueue.cpp ***/NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); }}native层的Looper和java层的Looper是对应的,主要负责唤醒java层的Looper,后面会讲到唤醒原理。
2.mHandler = new Handler()
Handler有多个构造方法,主要分为两类,一类带有Looper参数,构造时可以直接把Looper对象传进去;另一类不带Looper参数,这一类构造会调用到下面这个隐藏的构造方法。
/*** Handler.java ***/public Handler(Callback callback, boolean async) { 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;}由上面的代码可知,
new Handler之前必须调用Looper.prepare()初始化Looper对象(主线程的Looper由系统初始化),否则会抛异常。
new Handler不带Looper参数时,Handler默认获取当前线程的Looper,即由哪个线程创建的Handler,发消息时,就是哪个线程来处理消息。
Handler对象还得到了Looper对应的消息队列。
3.Looper.loop()
调用Looper.loop()开启消息循环。loop()的主要逻辑如下
/*** Looper.java ***/public static void loop() { //获取当前线程的Looper对象 final Looper me = myLooper(); //获取Looper对象的消息队列 final MessageQueue queue = me.mQueue; for (;;) { //循环从消息队列中获取消息 //消息队列中没有消息,或者都是若干时间后才要处理的消息,就会阻塞在这里 //等有新的需要马上处理的消息或者到时间后,就会取出消息继续执行。 Message msg = queue.next(); //从消息队列取出消息后分发处理 msg.target.dispatchMessage(msg); //处理完消息后回收消息对象 msg.recycleUnchecked(); }}可以看出,Looper.loop()是一个循环,其后面的代码一般不会执行,除非调用了Looper的quit方法退出消息循环。
初始化完成,由于消息队列里还没有消息,所以线程阻塞在queue.next()等待被唤醒。
七、为何阻塞在queue.next()
/*** MessageQueue.java ***/Message next() { //mPtr指向NativeMessageQueue对象 final long ptr = mPtr; if (ptr == 0) { return null; } int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { //不知道是干啥的 Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null) { if (now < msg.when) { //下个消息还没有准备好,需要延时处理 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); //取出消息,并从消息队列移除 return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } } // Run the idle handlers. // 省略了 idle handlers 的相关处理 // 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(ptr, nextPollTimeoutMillis);这个函数最终会调用到native Looper的pollnce()函数
/*** Looper.cpp ***/Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { //... if (result != 0) { //... return result; } result = pollInner(timeoutMillis); }}pollInner这个函数比较长,而且涉及到了Linux的epoll系统调用接口和管道等概念,这里不做介绍,只截取了部分代码,如下
/*** Looper.cpp ***/int Looper::pollInner(int timeoutMillis) { //这里就是等待消息,真正阻塞的地方!!! int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); //some error if (eventCount < 0) { result = POLL_ERROR; goto Done; } //没有新消息,超时唤醒。 //说明消息队列里有delay的消息到时间该处理了。 if (eventCount == 0) { result = POLL_TIMEOUT; goto Done; } for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { //...... } }}总之,有新消息到来或超时唤醒时,都会从这个函数中返回到MessageQueue.next()函数中的nativePollOnce(ptr, nextPollTimeoutMillis)处,继续往下执行。
如果有消息需要马上处理(now >= msg.when),则返回到Looper.loop()中,进行消息分发处理,处理完后回收该消息。
否则更新超时时间,循环继续进入nativePollOnce(ptr, nextPollTimeoutMillis)等待。
八、何时被唤醒
上面说了,有新消息到来或超时时间到时,epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis)可以被唤醒。
有超时时间,说明已经有延时消息被发过来了,只是没有马上处理。那么什么时候会有消息发过来呢?
先来看一下java层消息的发送流程。
九、java层消息发送流程
发送消息有以下几种方法:
1.使用Message的sendToTarget():会调用Handler的sendMessage(Message msg)方法
2.使用Handler的sendXXX系列和postXXX系列:
sendMessage(Message msg)sendMessageDelayed(Message msg, long delayMillis)sendMessageAtTime(Message msg, long uptimeMillis)sendEmptyMessage(int what)sendEmptyMessageDelayed(int what, long delayMillis)sendEmptyMessageAtTime(int what, long uptimeMillis)post(Runnable r)postDelayed(Runnable r, long delayMillis)postAtTime(Runnable r, long uptimeMillis)//下面两个方法会将Message插入到消息队列的最前端sendMessageAtFrontOfQueue(Message msg)postAtFrontOfQueue(Runnable r)以上函数最终都会调用到下面的函数,将Message放到消息队列里去。
/*** Handler.java ***/private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
/*** MessageQueue.java ***/boolean enqueueMessage(Message msg, long when) { synchronized (this) { msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; //如果队列为空||插入到队首||消息处理时间早于队首消息处理时间 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //循环遍历队列里的消息,按处理时间先后顺序,将新消息插入到合适位置 for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; prev.next = msg; } //注意了注意了 wake wake if (needWake) { nativeWake(mPtr); } } return true;}需要说明的是,msg.when变量表示该消息在什么时间点(系统启动时间 SystemClock.uptimeMillis())处理。
Message在消息队列里是按msg.when排序的,需要先执行的会放队列前面。
将Message插入到消息队列后,如需wake,就会调用nativeWake(mPtr)函数。
那什么情况下需要wake呢?
synchronization barrier 同步分割栏
首先来了解一下什么是synchronization barrier。
(见MessageQueue的postSyncBarrier()方法和removeSyncBarrier(int token)方法。)
在Message中有同步消息和异步消息之分。
(见Message的setAsynchronous(boolean async)方法和isAsynchronous()方法)
同步分割栏也是一个Message类型的对象,只不过它的target为空,如上面代码中p.target == null就代表p是一个同步分割栏。
简单的说,如果消息队列里没有同步分割栏,同步和异步消息是没有区别的。
如果队列里有同步分割栏,则其后面的同步消息,即使时间到了,也不会被执行,除非同步分割栏被移除。而异步消息则不会受此限制。
把消息插入到消息队列时,以下两种情况才需要唤醒。
一种情况是,新消息插入队首。
另一种情况是,队首是一个同步分割栏,而新插入的消息之前没有异步消息。
其他情况说明消息队列中的消息正在被处理,或者新消息前有等待处理的消息,新消息的处理时间还没到,则不需要唤醒。
十、唤醒
/*** MessageQueue.java ***/nativeWake(mPtr);
/*** android_os_MessageQueue.cpp ***/static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->wake();}
/*** android_os_MessageQueue.cpp ***/void NativeMessageQueue::wake() { mLooper->wake();}
/*** Looper.cpp ***/void Looper::wake() { uint64_t inc = 1; ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t))); if (nWrite != sizeof(uint64_t)) { if (errno != EAGAIN) { ALOGW("Could not write wake signal, errno=%d", errno); } }}涉及到了Linux的epoll系统调用接口和管道等概念,不做介绍,有兴趣可以自行查阅资料。
这里往文件里写如内容,前面就会监听到,从而Looper::pollInner(int timeoutMillis)函数中的epoll_wait()函数就返回了。
到此唤醒Looper线程。
十一、分发消息
Looper线程唤醒后,从消息队列取出消息,然后进行分发处理。
msg.target.dispatchMessage(msg);Message的成员变量target就是要处理该Message的目标Handler,消息分发函数如下
/*** Handler.java ***/public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}前面说了,发送消息使用Handler的sendXXX方法和postXXX方法。
上面msg.callback是使用Handler的postXXX方法发送消息时的参数Runnable,
若使用postXXX方法发送消息,则分发消息时,调用该Runnable的run()函数处理消息。
若使用sendXXX方法发送消息,则分发消息时,调用Handler的handleMessage(Message)函数处理消息。
十二、总结
简单总结一下:java层和native层各有一个Looper。
java层Looper:维护一个消息队列,不断循环从消息队列中取消息并处理。队列中没有消息时等待,其他线程将消息插入消息队列后,Looper并不是马上取出消息处理,而是等待唤醒。
native层Looper:与java层的对应,使用Linux的epoll系统调用,监听事件发生(文件有内容写入),唤醒java层Looper。
消息队列:中的消息是按处理时间排序。
其他线程将消息插入到消息队列中,然后根据需要,调用native接口唤醒java层Looper处理消息。
对于延时消息,由MessageQueue计算延时时间,利用epoll_wait的超时机制,超时唤醒。
十三、参考
Android Looper类代码分析(含 epoll系统调用接口简介)
Looper中的睡眠等待与唤醒机制(native Looper)
Android应用程序消息处理机制(Looper、Handler)分析
- Android Looper机制简介
- Android Looper简介
- Android中Looper简介
- android Looper Handler机制
- Android Native Looper机制
- Android Native Looper机制
- android looper 机制
- android消息处理机制-------Looper
- Android Handler Message Looper 机制
- Android消息机制之Looper
- Android之Looper-Handler机制
- Android Java层 Looper 机制
- Android消息机制之一---Looper
- Android Handler,Looper,MessageQueue 机制
- android消息处理机制--Looper
- Android Hanlder-Message-Looper机制
- Handler消息传递机制之Looper简介
- android消息处理机制学习(一)-Handler,Message,MessageQueue,Looper简介
- jsp获取当前最新浏览器
- 原型模式深入--使用序列化机制实现对象的深克隆
- ios常见错误日志
- 应用再签名
- Padding 与Margin
- Android Looper机制简介
- js刷新整个页面包括引入进来的jsp
- DELPHI 指针使用
- VS开发人员命令界面查看C++类内存布局
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- 回味经典——uboot1.1.6 之 第一阶段
- 淘宝省市区获取
- coreseek配置及使用
- 一文让你彻底了解iOS字体相关知识