Android技术知识Android开发Android精选

Android 消息机制详解(Android P)

2019-01-31  本文已影响86人  SharryChoo

前言

Android 消息机制,一直都是 Android 应用框架层非常重要的一部分,想更加优雅的进行 Android 开发,我想了解消息机制是非常必要的一个过程,此前也分析过很多次 Handler 消息机制, 不过都是浮于 Java 层,对于底层的源码并没有深究,经过一年的努力,笔者对于 Android 应用框架层有了新的认识和理解,这里重新写下这篇文章。

本文主要从以下几点来阐述 Androd 消息机制

一. 线程消息队列创建入口

我们知道, 应用进程主线程初始化的入口是在 ActivityThread.main() 中, 我们看看他是如何构建消息队列的

public class ActivityThread {

    static volatile Handler sMainThreadHandler;  // set once in main()

    public static void main(String[] args) {
        ......
        // 1. 做一些主线程消息循环的初始操作
        Looper.prepareMainLooper();
        
        ......
        
        // 2. 启动消息循环
        Looper.loop();
    }
    
}

好的, 可以看到 ActivityThread 中的消息循环构建过程如下

接下来我们先看看准备操作中做了些什么

二. 消息循环的准备

public final class Looper {

    private static Looper sMainLooper;  // guarded by Looper.class
    
    public static void prepareMainLooper() {
        // 1. 调用 prepare 方法真正执行主线程的准备操作
        prepare(false);
        
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 2. 调用了 myLooper 方法, 获取一个 Looper 对象给 sMainLooper 赋值
            sMainLooper = myLooper();
        }
    }

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

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 1.1 new 了一个 Looper 对象
        // 1.2 将这个 Looper 对象写入 ThreadLocal 中
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    public static @Nullable Looper myLooper() {
        // 2.1 通过 ThreadLocal 获取这个线程中唯一的 Looper 对象
        return sThreadLocal.get();
    }
    
    final MessageQueue mQueue;
    final Thread mThread;
    
    private Looper(boolean quitAllowed) {
        // 创建了一个消息队列
        mQueue = new MessageQueue(quitAllowed);
        // 获取了当前的线程
        mThread = Thread.currentThread();
    }
    
}

可以看到 Looper.prepareMainLooper 中主要做了如下几件事情

好的, 可以看到在创建 Looper 对象的时候, 同时会创建一个 MessageQueue 对象, 将它保存到 Looper 对象的成员变量 mQueue 中, 因此每一个 Looper 对象都对应一个 MessageQueue 对象

我们接下来看看 MessageQueue 对象创建时, 做了哪些操作

MessageQueue 的创建

public final class MessageQueue {
    
    private final boolean mQuitAllowed; // true 表示这个消息队列是可停止的
    private long mPtr;                  // 描述一个 Native 的句柄
    
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        // 获取一个 native 句柄
        mPtr = nativeInit();
    }
    
    private native static long nativeInit();

}

好的, 可以看到 MessageQueue 对象在创建的过程中, 会调用 nativeInit 来获取一个 native 的句柄, 我们看看这个 nativeInit 做了哪些操作

// frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    // 1. 创建了一个 NativeMessageQueue 对象
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ......
    // 增加 env 对它的强引用计数
    nativeMessageQueue->incStrong(env);
    // 2. 将这个 NativeMessageQueue 对象强转成了一个句柄返回 Java 层
    return reinterpret_cast<jlong>(nativeMessageQueue);
}


class NativeMessageQueue : public MessageQueue, public LooperCallback {
public:
    NativeMessageQueue();
    ......
private:
    JNIEnv* mPollEnv;
    jobject mPollObj;
    jthrowable mExceptionObj;
};

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    // 1.1 尝试调用 Looper.getForThread 获取一个 C++ 的 Looper 对象
    mLooper = Looper::getForThread();
    // 1.2 若当前线程没有创建过 Looper, 则走这个分支
    if (mLooper == NULL) {
        // 创建 Looper 对象
        mLooper = new Looper(false);
        // 给当前线程绑定这个 Looper 对象
        Looper::setForThread(mLooper);
    }
}

好的可以看到 nativeInit 方法主要做了如下的操作

可以看到这里的流程与 Java 的相反

接下来看看这个 C++ 的 Looper 对象在实例化的过程中做了哪些事情

Looper(Native) 的实例化

// system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    // 1. 创建 pipe 管道, 返回该管道文件读写的描述符
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    AutoMutex _l(mLock);
    // 处理 epoll 相关事宜
    rebuildEpollLocked();
}

void Looper::rebuildEpollLocked() {
    ......
    // 2. 创建一个 epoll 对象, 将其文件描述符保存在成员变量 mEpollFd 中
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    // 3. 将 pipe 管道的文件读写描述符 mWakeEventFd, 添加到 epoll 中, 让 epoll 对该管道的读写进行监听
    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;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    ......
}

好的, 可以看到 Looper 实例化时做了如下的操作

可以看到这里引入了两个新的名词, pipe 管道管道监听管理对象 epoll

相互依赖的结构图

到这里消息循环的准备工作就已经完成了, 这里分析一下它们的结构图

消息循环相互依赖关系图

三. 消息循环的启动

public final class Looper {
    
    public static void loop() {
        // 1. 获取调用线程的 Looper 对象
        final Looper me = myLooper();
        ......
        // 2. 获取 Looper 对应的消息队列
        final MessageQueue queue = me.mQueue;
        // 3. 死循环, 不断的处理消息队列中的消息
        for (;;) {
            // 3.1 获取消息队列中的消息, 取不到消息会阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 若取到的消息为 null, 这个 Looper 就结束了
                return;
            }
            ......
            try {
                // 3.2 处理消息
                msg.target.dispatchMessage(msg);
            } finally {
                ......
            }
            ......
        }
    }
    
}

好的, 可以看到 Looper 的 loop 方法主要做了如下几件事情

好的, 可以看到获取消息的方式是通过 MessageQueue.next 拿到的, 我们接下来看看它是如何获取的

从队列中取消息

public final class MessageQueue {
    
    Message next() {
        // 获取 NativeMessageQueue 的句柄
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        // 描述空闲事件处理者的数量, 初始值为 -1 
        int pendingIdleHandlerCount = -1; 
        // 描述当前线程没有新消息处理时, 可睡眠的时间
        int nextPollTimeoutMillis = 0;
        // 死循环, 获取可执行的 Message 对象
        for (;;) {
            // 1. 若 nextPollTimeoutMillis 不为 0, 则说明距离下一个 Message 执行, 有一定的时间间隔
            if (nextPollTimeoutMillis != 0) {
                // 在空闲期间, 执行 Binder 通信相关的指令
                Binder.flushPendingCommands();
            }
            // 2. 这里调用了 nativePollOnce, 在 native 层检查消息队列中是否有 msg 可读, 若无可执行的 msg, 则执行线程的睡眠, 时间由 nextPollTimeoutMillis 决定
            nativePollOnce(ptr, nextPollTimeoutMillis);
            // 3. 取队列中下一条要执行的 Message 对象, 并返回出去
            synchronized (this) {
                final long now = SystemClock.uptimeMillis(); // 获取当前时刻
                Message prevMsg = null;
                Message msg = mMessages;
                // 3.1 移除消息队列中无效的 Message 
                // Condition: msg 不为 null & msg 的处理者为 null
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                // 3.2 队列首部存在有效的 msg 
                if (msg != null) {
                    // 3.2.1 若当前的时刻 早于 队首消息要执行的时刻
                    if (now < msg.when) {
                        // 给 nextPollTimeoutMillis 赋值, 表示当前线程, 可睡眠的时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 3.2.2 若当前的时刻 不小于 队首消息要执行的时刻
                        mBlocked = false;// 更改标记位置
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        // 将队首消息返回出去
                        return msg;
                    }
                } else {
                    // 3.3 说明消息队列中无消息, 则给 nextPollTimeoutMillis 置为 -1, // 表示可以无限睡眠, 直至消息队列中有消息可读
                    nextPollTimeoutMillis = -1;
                }
                // 4. 获取一些空闲事件的处理者
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                // 若无空闲事件, 则进行下一次 for 循环
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }
                .......
            }

            // 4.1 处理空闲事件
            ......
            
            // 4.2 走到这里, 说明所有的空闲事件都已经处理好了
            // 将需要处理的空闲事件,置为 0 
            pendingIdleHandlerCount = 0;

            // 5. 因为处理空闲事件是耗时操作, 期间可能有新的 Message 入队列, 因此将可睡眠时长置为 0, 表示需要再次检查
            nextPollTimeoutMillis = 0;
        }
    }
    
    // native 方法
    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
 
}

可以看到 MessageQueue.next 内部维护了一个死循环, 用于获取下一条 msg, 这个 for 循环做了如下的操作

至此一次 for 循环就结束了, 可以看到 Message.next() 中其他的逻辑都非常的清晰, 但其睡眠是一个 native 方法, 我们继续看看它的内部实现

消息队列中无消息时线程睡眠的实现

// 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
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    ......
    // 1. 调用了 Looper 的 pollOne
    mLooper->pollOnce(timeoutMillis);
    ......
}

// system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        ......
        // result 不为 0 说明读到了消息
        if (result != 0) {
            ......
            return result;
        }
        // 2. 若未读到消息, 则调用 pollInner
        result = pollInner(timeoutMillis);
    }
}


int Looper::pollInner(int timeoutMillis) {
    ......
    int result = POLL_WAKE;
    ......
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    // 3. 调用 epoll_wait 来监听 pipe 中的 IO 事件, 若无事件, 则睡眠在文件读操作上, 时长由 timeoutMillis 决定
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    // 4. 走到这里, 说明睡眠结束了
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;              // 获取文件描述
        uint32_t epollEvents = eventItems[i].events;
        // 5. 若为唤醒事件的文件描述, 则执行唤醒操作
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken();// 唤醒
            } else {
                ......
            }
        } else {
           ......
        }
    }
    ......
    return result;
}

好的可以看到 JNI 方法 nativePollOnce, 其内部流程如下

好的, 至此线程是睡眠的机制也明了了, 这里通过 UML 图总结一下, 线程消息队列的创建与循环

线程消息的创建与循环的流程图

四. 消息的发送与处理

我们都知道, Android 系统提供了 Handler 类, 描述一个消息的处理者, 它负责消息的发送与处理

public class Handler {
   
   final Looper mLooper;
   final MessageQueue mQueue;
   final Callback mCallback;
   
   public Handler() {
       this(null, false);
   }
   
   public Handler(Callback callback, boolean async) {
       // 调用该方法的线程的 Looper
       mLooper = Looper.myLooper();
       // 这里扔出了 Runtime 异常, 因此 Handler 是无法在没有 Looper 的线程中执行的
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread " + Thread.currentThread()
                       + " that has not called Looper.prepare()");
       }
       // 获取消息队列
       mQueue = mLooper.mQueue;
       // 给 Callback 赋值
       mCallback = callback;
       ......
   }
   
   public final boolean sendMessage(Message msg){
       ......
   }
   
   public void handleMessage(Message msg) {
   
   }
   
}

好的, 可以看到 Handler 的结构如上述代码所示, 本次只 focus 以下三个方法

好的, 接下来我们先看看如何发送消息的

消息的发送

我们先看看, Android 中的消息是如何发送的

public class Handler {
   
   ......
   public final boolean sendMessage(Message msg) {
       // 回调了发送延时消息的方法
       return sendMessageDelayed(msg, 0);
   }
   
   public final boolean sendMessageDelayed(Message msg, long delayMillis) {
       if (delayMillis < 0) {
           delayMillis = 0;
       }
       // 回调了发送指定时刻消息的方法
       return sendMessageAtTime(msg, /*指定时刻 = 当前时刻 + 延时时间*/SystemClock.uptimeMillis() + delayMillis);
   }
   
   public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
       MessageQueue queue = mQueue;
       ......
       // 回调了入消息队列的方法
       return enqueueMessage(queue, msg, uptimeMillis);
   }
   
   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
       // 将这个消息的处理者, 设置为其自身
       msg.target = this;
       ......
       // 调用 MessageQueue 的 enqueueMessage 执行入队列的操作
       return queue.enqueueMessage(msg, uptimeMillis);
   }
}

可以看到发送消息的操作, 进过了一系列方法的调用, 会走到 sendMessageAtTime 中, 表示发送指定时刻的消息, 然后会调用 enqueueMessage 执行入消息队列操作

接下来看看 MessageQueue.enqueueMessage 做了哪些操作

public final class MessageQueue {

    boolean enqueueMessage(Message msg, long when) {
        ......
        synchronized (this) {
            ......
            msg.when = when;           // 将消息要执行的时刻写入成员变量
            Message p = mMessages;     // 获取当前队首的消息
            boolean needWake;          // 描述是否要唤醒该 MessageQueue 所绑定的线程
            // Case1: 
            // 1. p == null, 队列为空
            // 2. 入队列消息需要立即执行
            // 3. 入队列消息执行的时间 早于 当前队首消息执行的时间
            if (p == null || when == 0 || when < p.when) {
                // 将该消息放置到队首
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;  // 队首元素变更了, 有可能需要立即执行
            } else {
            // Case2: 入队列的消息执行时间 晚于 队首消息执行时间
                ......
                // 将该消息插入到消息队列合适的位置
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    // 将 needWake 置为 false, 因为该消息插入到了后面, 因此不需要唤醒
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p;
                prev.next = msg;
            }

            // 处理该消息队列绑定线程的唤醒操作
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    
    private native static void nativeWake(long ptr);

}

可以看到 MessageQueue 在执行消息入队列时, 做了如下操作

消息入队列的过程还是很清晰明了的, 从上一篇文章的分析中我们知道, 若 MessageQueue 在当前时刻没有要执行的消息时, 会睡眠在 MessageQueue.next() 方法上, 接下来看看它是如何通过 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();
}

// system/core/libutils/Looper.cpp
void Looper::wake() {
    // 向 Looper 绑定的线程 pipe 管道中写入一个新的数据
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    ....
}

可以看到, nativeWake 是通过向 Looper 绑定的线程 pipe 管道中写入一个新的数据的方式唤醒目标线程的

消息的处理

通过上一篇的分析可知, 当 MessageQueue.next 返回一个 Message 时, Looper 中的 loop 方法便会处理消息的执行, 先回顾一下代码

public final class Looper {
    
    public static void loop() {
        final Looper me = myLooper();
        ......
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 若取到的消息为 null, 这个 Looper 就结束了
                return;
            }
            ......
            try {
                // 处理消息
                msg.target.dispatchMessage(msg);
            } finally {
                ......
            }
            ......
        }
    }
    
}

好的, 可以看到当 MessageQueue.next 返回一个 Message 时, 便会调用 msg.target.dispatchMessage(msg) 去处理这个 msg

接下来我们看看 Handler 处理消息的流程

 public class Handler {
     
    public void dispatchMessage(Message msg) {
        // 1. 若 msg 对象中存在 callback, 则调用 handleCallback
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 2. 若当前 Handler 设值了 Callback, 进入这个分支
            if (mCallback != null) {
                // 2.1 若这个 Callback 的 handleMessage 返回 true, 则不会将消息继续向下分发
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 3. 若消息没有被 mCallback 拦截, 则会调用 handleMessage 进行最后的处理
            handleMessage(msg);
        }
    } 
    
    /**
     * 方式一: 优先级最高
     */
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    public interface Callback {
        /**
         * 方式二: 优先级次高
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
     
    public Handler(Callback callback, boolean async) {
        ......
        // Callback 由构造函数传入
        mCallback = callback;
        ......
    } 
    
    /**
     * 方式三: 这个处理消息的方式, 由子类重写, 优先级最低
     */
    public void handleMessage(Message msg) {
    
    }
    
 }

可以看到 Handler 的 dispatchMessage 处理消息主要有三种方式

好的, 消息的发送和处理到这里就分析结束了, 最后再了解一下 MessageQueue 中空闲处理者的相关知识

空闲处理者的添加与处理

1. 什么是空闲处理者

通过上一篇文章的分析可知 MessageQueue 通过 next 方法通过死循环获取下一个要处理的 Message, 若当前时刻不存在要处理的消息, 下次循环会进行睡眠操作

public final class MessageQueue {

   Message next() {
        for (;;) {
            // 睡眠操作
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                Message prevMsg = null;
                Message msg = mMessages;
                ......
                if (msg != null) {
                    // ...... 
                    return msg;
                }
                
                // 空闲时间
                ....... 
            }

            // 空闲时间
            ......
            
        }
    }
    
}

2. 空闲处理者的添加

public final class MessageQueue {
    
    public static interface IdleHandler {
        /**
         * 处理空闲消息
         */
        boolean queueIdle();
    }
    
    // 空闲消息集合
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    
    public void addIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }
    
}

通过上述代码可以得到以下的信息

好的, 结下来看看处理细节

3. 空闲消息的处理

public final class MessageQueue {

    // 空闲消息集合
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    // 空闲消息处理者的数组
    private IdleHandler[] mPendingIdleHandlers;
    
    Message next() {
        ...... 
        for (;;) {
            ......
            synchronized (this) {
                // 省略获取 msg 的代码
                ......
                // 1. 从空闲消息集合 mIdleHandlers 中获取 空闲处理者 数量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                // 2 若无空闲处理者, 则进行下一次 for 循环
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }
                ......
                // 3. 将空闲消息处理者集合转为数组
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 4. 处理空闲消息
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];// 获取第 i 给位置的空闲处理者
                mPendingIdleHandlers[i] = null; // 置空
                boolean keep = false;        
                try {
                    // 4.1 处理空闲消息
                    keep = idler.queueIdle(); 
                } catch (Throwable t) {
                    ......
                }
                if (!keep) {
                    synchronized (this) {
                        // 4.2 走到这里表示它是一次性的处理者, 从 mIdleHandlers 移除
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            ......
        }
    }
    
}

好的, 可以看到 MessageQueue.next 在获取不到 msg 时, 会进行一些空闲消息的处理

总结

至此 Android 的消息机制就全部结束了, 此前分析过消息机制, 但一直没有深度到 Native 层, 只是浮于表面, 本次深入到了 Native 层, 看到了更底层的 epoll 监控管道相关的知识, 可以说发现了新的天地, 对 Handler 的机制有了更加深刻的理解, 本文中有分析的不正确或者不到位的地方, 希望大家多多批评指出, 笔者希望与大家共同成长。

上一篇下一篇

猜你喜欢

热点阅读