美文网首页线程Android开发Android技术知识
Android源码:Handler, Looper和Messag

Android源码:Handler, Looper和Messag

作者: darkengine | 来源:发表于2017-03-09 16:06 被阅读272次

    做了6年的Android开发,此间做的事情非常杂包括ROM,SDK和APP,从来没有好好的研究过Android的基础代码。趁着这段时间项目没那么忙,把基础的东西仔细研究清楚并记录下来。

    想到要分析Looper和Handler的时候其实正在看背光调节模块的代码,其中看到PowerManagerService中很多API最终是向Handler发送Message的形式进行实际操作。Handler在做App的时候用的很多,最常见的用法就是extend一个Handler类然后重载其handleMessage方法,然后要发消息的时候调用其sendMessage API即可。使用Handler还可以在不同的线程之间传递消息实现异步操作,下边我们扒一扒Android N的源码看看其实现以及Handler, Looper以及Thread之间的关系。

    一,Looper, Thread之间的关系

    先来看一段代码

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        Handler h1 = new Handler();
    
        new Thread(new Runnable() {
            @Override public void run() {
                Handler h2 = new Handler();
            }
        }).start();
    }
    

    运行之后h1正常创建,但是创建h2的时候crash了:

    --------- beginning of crash
    E/AndroidRuntime: FATAL EXCEPTION: Thread-263
    Process: com.example.stone.sfsandroidclient, PID: 32286
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
       at android.os.Handler.<init>(Handler.java:200)
       at android.os.Handler.<init>(Handler.java:114)
       at com.example.stone.sfsandroidclient.MainActivity$1.run(MainActivity.java:71)
       at java.lang.Thread.run(Thread.java:818)
    

    出错日志提示不能在一个没有调用过Looper.prepare()的Thread里边new Handler()。为什么Looper.prepare()会影响Handler的创建呢,先看源码哪里会抛出这个异常:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    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()");
        }
        ... ...
    }
    
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    这个sThreadLocal是一个ThreadLocal(线程局部)对象,它有get()/set()这么一对方法。它的工作机制是,仅当一个线程调用了它的set()来设置值之后,get()才能获取到设置进去的对象,而且这个对象是线程唯一的。也就是说在ThreadA里面调用了sThreadLocal.set(),在ThreadB里面调用sThreadLocal.get()还是null。所以在Looper.prepare()里一定调用了set():

    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));
    }
    
    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而我们自己new的Thread就没有呢,因为在主线程里边有这段(代码):

    public static void main(String[] args) {
        ... ...
        Looper.prepareMainLooper();
        ... ...
    }
    

    所以主线程是“生来”就有一个Looper跟它绑定在一起的,而且这个looper身份很特殊,它是整个进程里面的main looper,通过Looper.getMainLooper()就能拿到,接着就可以为所欲为的做些更新UI的活儿了。
    那么其他的Thread要怎样才能也拥有一个Looper么,Android为我们提供了一个很好的例子 - HandlerThread类,用法如下:

    HandlerThread ht = new HandlerThread("worker");
    ht.start();
    Handler handler = new Handler(ht.getLooper());
    

    关键就在HandlerThread的run里面:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

    从上述分析可知Looper.prepare()之后这个Thread就有了一个跟它关联在一起的Looper了,这里头有两个需要注意的点:一是new Handler()要在ht.start()之后,二是如果要extend HandlerThread则记得要在run()里面调用super.run()。

    二, Looper, Handler和MessageQueue

    还是看Handler的构造函数

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

    可知它们的关系为,一个HandlerThread带有一个Looper,Looper有一个MessageQueue和多个与之关联的Handler,而一个Handler只与一个Looper有关联。UML类图如下:

    handler_looper.png

    在使用中接触得最多的是Message,它在MessageQueue里面以单项链表的形式组织,mMessages指向链表头部。每个Java层的MessageQueue都有一个C++实现的NativeMessageQueue与之对应,关键的实现部分其实在native层中,下边的章节会分析。

    三,工作机制

    先借一张《Efficient Android Threading》里面的图:

    looper.png

    顾名思义,Looper的主要执行过程是无线循环的loop(),它在HandlerThread的run()运行起来:

    public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
    
        for (;;) {
            // queue.next()是阻塞调用,后面详细分析
            Message msg = queue.next(); // might block
            // 当next()返回null的时候,表示Looper.quit()被调用了所以退出无限循环
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ... ...
            try {
                // 找到了msg就交给target(Handler)开干吧
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            // Message可回收利用,这里不展开了
            msg.recycleUnchecked();
        }
    }
    

    Handler的dispatchMessage()就没什么可说的了:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    

    里面要么就是执行msg自己定义的callback,要么就是在extend Handler时定义的handleMessage方法。要记得的就是这个方法的调用是在Handler关联的Looper所关联的线程里面执行的。所以如果你的Handler关联的是main Looper,就不要在handleMessage()里面做耗时的操作(网络请求,IO,密集计算等)了,否则很有可能造成ANR。
    接着分析最关键的queue.next():

    Message next() {
        ... ...
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // 阻塞在native层
            nativePollOnce(ptr, nextPollTimeoutMillis);
            // mMessages有可能被其他线程修改(通过sendMessage),锁起来
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 这是一个有故事的msg因为target为null表示这个一个barrier(围栏)
                    // barrier可以阻止卡住所有sync类型的msg的执行,它通过postSyncBarrier()插入,
                    // 通过removeSyncBarrier()去除,当barrier去除后所有被拦住的sync msg都被依次执行
                    // 目前源码中用到了barrier的就只有view的invalidation机制了。
                    // 如果特别重要的msg还是有特权可以执行的,async msg就是这个例外。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    // 当没有barrier而且有sync msg,或者有barrier但是找到async msg的时候
                    if (now < msg.when) {
                        // msg定义的when还没到,让native继续等nextPollTimeoutMillis时长
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 有到点的msg,返回给Looper的loop()处理
                        mBlocked = false;
                        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 {
                    // 没有msg了,在native层阻塞
                    nextPollTimeoutMillis = -1;
                }
    
                // 如果quit()调用了,通知Looper.loop()退出无限循环
                if (mQuitting) {
                    dispose();
                    return null;
                }
            ... ...
            // 在idle handler执行的时候有可能来了新的mesasge,设为0不阻塞马上进行检查
            nextPollTimeoutMillis = 0;
        }
    }
    

    捎带讲讲barrier的插入/拔出,没兴趣的可以略过毕竟用得少:

    private int postSyncBarrier(long when) {
        // 将barrier插入到mMessges队列中,回收回来的msg的target是空的
        // 把token返回给调用者,拔掉barrier的时候是需要token的
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
    
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
    
    public void removeSyncBarrier(int token) {
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            // 对一下token
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();
    
            // 拔出barrier唤醒阻塞在native的looper所在线程
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }
    

    四, native层的NativeMessageQueue机制

    static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
            jlong ptr, jint timeoutMillis) {
        NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
        nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
    }
    
    void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
        mPollEnv = env;
        mPollObj = pollObj;
        // 交给native looper去poll了
        mLooper->pollOnce(timeoutMillis);
        mPollObj = NULL;
        mPollEnv = NULL;
        ... ...
    }
    

    native looper使用了linux的epoll机制:

    int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
        int result = 0;
        for (;;) {
            // native looper实现了很复杂的功能,可以从fd中读取远端响应并返回
            // 但是MessageQueue只用到了其阻塞的特点,所以这里忽略了一大段代码
            ... ...
            if (result != 0) {
    #if DEBUG_POLL_AND_WAKE
                ALOGD("%p ~ pollOnce - returning result %d", this, result);
    #endif
                if (outFd != NULL) *outFd = 0;
                if (outEvents != NULL) *outEvents = 0;
                if (outData != NULL) *outData = NULL;
                return result;
            }
    
            result = pollInner(timeoutMillis);
        }
    }
    
    int Looper::pollInner(int timeoutMillis) {
        ... ...
        // 同样,这样删了更大一段代码因为我们只关心阻塞
        int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    
        // 不管返回什么,不阻塞的返回就好
        if (eventCount < 0) {
            ... ...
            goto Done;
        }
    
        // Check for poll timeout.
        if (eventCount == 0) {
            ... ...
            goto Done;
        }
        ... ...
    Done: ;
        ... ...
        return result;
    }
    

    这里最最重要的就是epoll_wait()调用,在linux shell里面man epoll_wait得到提示:

    NAME
           epoll_wait, epoll_pwait - wait for an I/O event on an epoll file descriptor
    
    SYNOPSIS
           #include <sys/epoll.h>
    
           int epoll_wait(int epfd, struct epoll_event *events,
                          int maxevents, int timeout);
    ... ...
    RETURN VALUE
           When successful, epoll_wait() returns the number of file descriptors ready for the requested I/O, or zero if no file  descrip‐
           tor  became  ready  during the requested timeout milliseconds.  When an error occurs, epoll_wait() returns -1 and errno is set
           appropriately.
    

    其中timeout即Looper中的timeoutMillis即Java层的MessageQueue.next()中的nextPollTimeoutMillis。重新看next()中nextPollTimeoutMillis有3种可能的选择:0, -1或(int) Math.min(msg.when - now, Integer.MAX_VALUE);即最近一个msg的到期时间,传到epoll_wait()中产生的效果分表是马上返回、一直阻塞直至mEpollFd可用、阻塞msg.when-now的时长或者mEpollFd可用。这样就达到了闲时线程阻塞交出CPU,msg到期之后Thread接着执行的效果。当Thread正在永久阻塞的时候,还可以通过让mEpollFd变为可用来通知线程跳出阻塞。

    五,Native Looper的核心 - epoll

    还是只关心阻塞和唤醒那部分,首先要先了解linux的epoll机制,epoll需要三个linux系统调用包括上边的epoll_wait还有epoll_create以及epoll_ctl,看看native Looper是使用这些系统调用的:

    Looper::Looper(bool allowNonCallbacks) :......{
        // 通过eventfd生成一个专门用于发射event的fd
        mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
        ... ...
        rebuildEpollLocked();
    }
    
    void Looper::rebuildEpollLocked() {
        // 通过epoll_create生成用于epoll_wait调用的fd
        mEpollFd = epoll_create(EPOLL_SIZE_HINT);
        LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
    
        struct epoll_event eventItem;
        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = EPOLLIN;
        eventItem.data.fd = mWakeEventFd;
        // 通过epoll_ctl将mEpollFd和mWakeEventFd联系起来
        int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    
    }
    

    至此,mEpollFd和mWakeEventFd就变成了一对好基友,当向mWakeEventFd写入数据的时候 阻塞在epoll_wait(mEpollFd...);的线程将会被唤醒(向mWakeEventFd写入event的话epoll_wait还能读取到该event,我们只关心唤醒所以不作进一步解析)。所以native Looper的wake()是这样实现的:

    void Looper::wake() {
    #if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ wake", this);
    #endif
    
        uint64_t inc = 1;
        // 向mWakeEventFd里面写了个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: %s", strerror(errno));
            }
        }
    }
    

    想要了解更多可以man epoll_ctl, man epoll_create以及查找linux epoll,管道pipe的实现机制。总的来说,Android的Looper设计的是相当的精巧。

    相关文章

      网友评论

        本文标题:Android源码:Handler, Looper和Messag

        本文链接:https://www.haomeiwen.com/subject/hdvfpttx.html