Android 基于Handler 剖析消息机制

版本基于:Android R

0. 前言

Android handler 处理消息机制是Android 系统、应用开发中一个十分重要的利器,可以简单、轻松的将事情异步分解处理。

Handler 与LooperMessageQueueMessage类紧密结合,实现了完整的消息机制。

1. 框架

注意:

Handler 发消息时,MessageQueue 是固定的,而MessageQueue 是在固定线程的固定Looper 中,所以最终使用哪个线程的Handler 发消息,最终处理函数handleMessage() 也应该是在哪个线程中运行

2. Message

装载这消息的所有信息

  • what: 消息的类别
  • when:消息触发时间
  • arg1,arg2,obj,data:消息的具体数据
  • target:消息的去向,Handler 对象,消息可能通过别的Handler 对象进行send,但是取出msg dispatch的时候一定是通过这个target 去handleMessage
  • callback:消息的Runnable,类似handleMessage

还需要注意是Message 有个全局的 sPool,最大限度为MAX_POOL_SIZE(默认 50) 个,如果小于MAX_POOL_SIZE,回收后的Message 会存放在sPool 中,所以,如果临时需要Messag 最好通过obtain() 的方式获取,节省内存同时也提高了性能。

3. Handler

3.1 Handler 构造

Handler 类的构造函数有7 个,但是最终可以提炼出两个,以参数有无looper 区分。

frameworks/base/core/java/android/os/Handler.java

    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

核心的几个变量:

  • mLooper 如果不是由外面传入,那么Handler 的整个处理过程都在本线程中进行;如果由外面传入,则可以实现跨线程消息传输;
  • mQueue 通过mLooper 获得,在sendMessage() 时需要将message 传入MessageQueue,MessageQueue 是由Looper 创建的;
  • mCallback 为消息处理的核心函数,即handleMessage()

在简单的解析Handler 的构造后,了解一些基本的概念:

  • Message: 消息,可能有硬件产生,也可能由软件产生,传输的基本单元;
  • MessageQueue:消息队列,存放Message,是整个消息机制的核心类,需要串联整个消息流程;
  • Looper:提供循环,一直处理消息的场所;
  • Handler:消息机制的辅助类,消息的发起、处理都是通过Handler;

3.2 sendMessageAtTime

frameworks/base/core/java/android/os/Handler.java
 
   public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ...
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

通过Handler 发送消息,无论是post(),还是sendMessage(),最后调用的是sendMessageAtTime() 函数,而在该函数中最终通过 MessageQueue.enqueueMessage() 将message 添加到 queue 中。详细看下面的第 5.5 节。

4. Looper

4.1 Looper 构造

frameworks/base/core/java/android/os/Looper.java

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

Java 端的Looper 构造用来创建Java 端的MessageQueue 实例,Looper 根本上只是又来提供一个循环,Looper 永远是活在创建它的线程中。

但是,JAVA Looper 的构造函数是private,只能通过prepare 接口获取。

4.2 prepare()

frameworks/base/core/java/android/os/Looper.java

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

在代码中,变量 sThreadLocal 的类型是ThreadLocal,该类的作用是提供“线程局部存储”,那什么是线程局部存储(TLS) 呢?这个问题可以从变量的作用域的角度来理解。

变量常见作用域分为以下几种:

  • 函数内部的变量:其作用域是该函数,在函数调用的时候,该变量会重新初始化。
  • 类内部的变量:其作用域是该类对象,即只要对象没有销毁,那么这个变量在对象内部的值是不变的。
  • 类内部的局部变量:其作用域是整个进程,则只要在该进程中个,这个变量的值是一直保持,无论该类构造过多少对象,这个值是一样的。

对于类内部的变量而言,无论从进程中的哪个线程引用该变量,其值总是一样的。因为在编译器内部为这个局部变量独立分配了一个空间。但是,有时候我们想从一个线程中引用这个变量的值是一样的,不同的线程引用这个变量的时候值是不一样的,即我们需要一种作用域为线程变量定义,这就是”线程局部存储“。

libcoere/ojluni/src/main/java/java/lang/ThreadLocal.java

public class ThreadLocal<T> {
    protected T initialValue() {
        return null;
    }

    public ThreadLocal() {
    }

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    ...
}

在get 的时候最开始返回的value 为null,通过set 后将value 存入ThreadLocalMap 中。

上面是ThreadLocal.java 中的部分code,可以看出ThreadLocal是个模板,这样就方便各种不同的对象类型。例如,Looper中的 sThreadLocal 是这样定义的:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

还有个细节需要注意,不能反复的在同一个线程中调用prepare(),第二次调用的时候sThreadLocal 中已经有值了,会抛出exception。

另外,需要注意

Activity 的MainUI 线程默认是有消息队列的,所以在Activity 中新建Handler 时,不需要先调用Looper.prepare()。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper,要需要的时候,通过调用prepare函数来实现。

4.3 loop()

frameworks/base/core/java/android/os/Looper.java

    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
            ...            


            try {
                msg.target.dispatchMessage(msg);
                ...
            }
            ...

            msg.recycleUnchecked();
        }
    }
  • loop() 函数必须是在prepare函数之后调用,不然会有提示:
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  • for 循环是无限循环,会一直取消息,如果没有消息,会暂时的挂起来
  • msg.target.dispatchMessage(msg); msg.target 就是Handler
frameworks/base/core/java/android/os/Handler.java

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {//传入到message中
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) { //构造时传入
                    return;
                }
            }
            handleMessage(msg); //继承Handler.Callback 后重写了handleMessage
        }
    }

这部分逻辑,详细可以查看《Android 中Handler 详解》一文中的第 2 节。

Java 的Looper 比native 的简单些,Looper.java 中提供一个循环,通过 msg.next() 反复的取出MessagQueue 中的msg,然后通过handleMessage() 进行处理。而 msg.next()  是一个阻塞的接口,等待MessageQueue 中存放一个新的message 的时候,会将这个阻塞唤醒,后面解析MessageQueue 时详细说明阻塞、监听机制。

5. MessageQueue

5.1 构造

frameworks/base/core/java/android/os/MessageQueue.java

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
  • mQuitAllowed 标记是否允许quit;
  • 调用nativeInit 进行native 初始化;

下面会看到 MessageQueue 添加的message 都是存放在Java 端的,之所以引入native ,主要就是为了 epoll 机制使用。

5.2 nativeInit()

frameworks/base/core/jni/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);
}

nativeInit() 中,new了一个 Native 层的 MessageQueue 对象,并将其地址保存在了Java层MessageQueue 的成员 mPtr 中,Android中有好多这样的实现,一个类在 Java 层与 Native 层都有实现,通过JNI的 GetFieldID() 与 SetIntField() 把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如 Parcel 类

5.2.1 NativeMessageQueue的构造函数实现

frameworks/base/core/jni/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);
    }
}

在 NativeMessageQueue 的构造函数中,主要是确定 native 端的 Looper 对象,而该对象通过两种方式获取:

  • 线程局部变量,该Looper 已经创建好,并存在线程局部变量中;
  • new Looper() 

5.2.2 native Looper 的构造

system/core/libutils/Looper.cpp

Looper::Looper(bool allowNonCallbacks)
{
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    rebuildEpollLocked();
}
  • 创建一个wakeEventFd
  • 调用 rebuildEpollLocked(),初始化epoll

-->

继续来看rebuildEpollLocked()

void Looper::rebuildEpollLocked() {
    ...

    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC)); //新建一个epoll fd

    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.get();
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem); //监听mWakeEventFd
    ...
}

通过 epoll_create1() 创建一个 epoll fd,并通过 epoll_ctl() 将 mWakeEventFd 加入到监听。

整个消息机制主要是在有新消息添加时唤醒阻塞,所以其他的暂时不解释,主要关注mWakeEventFd

5.3 nativePollOnce()

在上面第 4.3 节中,loop() 是一个死循环,通过 MessageQueue.next() 函数,获取一下个 message 进行处理,而在 next() 函数中通过 nativePollOnce() 进行 native 端的 wait,从而达到 next() 的阻塞:

frameworks/base/core/jni/android_os_MessageQueue.cpp

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) {
    ...
    mLooper->pollOnce(timeoutMillis);
    ...

}

Looper->pollOnce() 的详细过程,这里不在过多的解释,后面有时间补充一个 Looper.cpp 的详细解析说明。

这里看wake 相关的部分:

system/core/libutils/Looper.cpp

int Looper::pollInner(int timeoutMillis) {
    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) { //如果timeout小于0时,无限延时阻塞
            timeoutMillis = messageTimeoutMillis;
        }

    }

    ...

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis); //等待唤醒或超时

    // No longer idling.
    mPolling = false;

    ...

    // Check for poll timeout.
    if (eventCount == 0) { //因为超时而唤醒epoll

        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.get()) { //wake fd 被写数据了,通过wake()进行唤醒
            if (epollEvents & EPOLLIN) {
                awoken(); //只是把wake fd 中的数据读出来,之后不做过多的处理返回
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ...
        }
    ...
    return result;
}

见代码中注释。

5.4 next()

前期4.3 节中提到在调用MessageQueue.next() 的时候会进入阻塞,这里来看下如何阻塞?

frameworks/base/core/java/android/os/MessageQueue.java

    Message next() {
        ...

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ...

            nativePollOnce(ptr, nextPollTimeoutMillis); //进入阻塞,但是如果nextPollTimeoutMillis为0时,表示立即唤醒

            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) { //注意barrier message
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); //如果唤醒的时候不够取出msg,循环回去继续阻塞
                    } else {
                        // Got a message.
                        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; //取出msg,回到4.3 节通过Handler 进行dispatch
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                ...
            }
            ...
        }
    }

5.5 enqueueMessage()

3.2 节得知通过Handler 发送消息,最终调用的是 MessageQueue.enqueueMessage():

frameworks/base/core/java/android/os/MessageQueue.java

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) { //必须指定Handler,后期的处理依赖它
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) { //这个message  不能已经被send过了
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) { //MessageQueue 不能处于quitting 状态
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse(); //send过来了,就标记上已使用
            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; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) { //如果没有唤醒,则立马唤醒epoll
                nativeWake(mPtr);
            }
        }
        return true;
    }

详细看代码中注释,核心部分就是nativeWake

5.6 nativeWake()

frameworks/base/core/jni/android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

void NativeMessageQueue::wake() {
    mLooper->wake();
}

Looper->wake() 就是向wake 的fd 中写数据用以唤醒epoll

-->

system/core/libutils/Looper.cpp

void Looper::wake() {
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    ...
}

向wake fd 写了1,用以唤醒epoll,这样就可以回到 4.3 节进行messag的dispatch。

至此,Android 重要的消息机制已经剖析完成,后续native Looper 或单个模块的注意点再以另外的新文分析。

总结下:

  • 通过Handler 向该线程的Looper 中MessageQueue 中添加message;
  • MessageQueue 中如果有新的消息,会将原来阻塞(epoll wait)的流程唤醒,继续执行Looper 流程,将取出的Message 发送到其中的target所在的线程中进行处理;

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值