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)分析


0 0