framework学习笔记

framework学习笔记11. Handler源码深入解析

2020-12-22  本文已影响0人  加个标志位

如果不了解handler消息机制,请阅读上一篇文章:Handler源码解析

相关源码文件:

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

framework/base/core/jni/android_os_MessageQueue.cpp
system/core/libutils/Looper.cpp
system/core/include/utils/Looper.h

在上一篇的 Handler 文章中,我们初步介绍了消息的入列和出列,并没有讲如何处理延时消息,那么本节就来分析一下 Handler 到底是怎么处理消息延迟的:假如我们要延迟 5s 再去处理这个消息。

  1. MessageQueue.next() 方法:
Message next() {
        // 判断 native 层的 MessageQueue 对象有没有正常创建
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        // 消息执行需要等待的时间
        int nextPollTimeoutMillis = 0;
        for (;;) {
            // 执行 native 层的消息延迟等待,第一次调 next 方法不会进来(不休眠)
            // 0表示立即执行;-1表示永久休眠;1000表示休眠1s
            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 {
                        // 不需要延迟获取已经过了时间,立马返回
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        // 标记为已在使用状态
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 如果队列里面没有消息,等待时间是 -1
                    nextPollTimeoutMillis = -1;
                }
                // 有没有空闲的 IdleHandler 需要执行,什么鬼???后面会讲
                // 这里目前分析是 == 0 ,跳出
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }
                ...
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

通过当前消息的执行时间与当前系统时间做比较,如果小于等于当前系统时间则立即返回执行该消息,如果大于当前系统时间则调用 nativePollOnce 方法去延迟等待被唤醒,当消息队列里面为空时则设置等待的时间为 -1。接下来Native 层的 framework/base/core/jni/android_os_MessageQueue.cpp
android_os_MessageQueue_nativePollOnce()方法:

// framework/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    // 通地址转换成 native 层的 MessageQueue 对象
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

//framework/base/core/jni/android_os_MessageQueue.cpp的内部类NativeMessageQueue
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    // 调用 native 层 Looper 对象的 pollOnce 方法
    mLooper->pollOnce(timeoutMillis);
}

// system/core/libutils/Looper.cpp
inline int pollOnce(int timeoutMillis) {
    return pollOnce(timeoutMillis, NULL, NULL, NULL); 
}

// system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        ...
        if (result != 0) {
            ...
            return result;
        }
        // 再处理内部轮询
        result = pollInner(timeoutMillis); 
    }
}

// system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
    ...
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;
    mPolling = true; //即将处于idle状态
    // fd最大个数为16
    struct epoll_event eventItems[EPOLL_MAX_EVENTS]; 
    // 等待事件发生或者超时,在 nativeWake() 方法,向管道写端写入字符,则该方法会返回;
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ...
    return result;
}

上述代码中,我们知道进入休眠是通过android_os_MessageQueue_nativePollOnce () 调用底层Looper.cpp中的pollInner(int timeoutMillis),最终在epoll_wait()处等待timeoutMillis;那么另一方面唤醒的方法是什么呢?答案在源码中非常明显,是android_os_MessageQueue_nativeWake() -> 对应MessageQueue.java中的native方法nativeWake(long ptr)。

// framework/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);
    return nativeMessageQueue->wake();
}

// framework/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
    mLooper->wake();
}

// system/core/libutils/Looper.cpp
void Looper::wake() {
    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, errno=%d", errno);
        }
    }
}

小结:native 层其实也有 Handler.cpp 、MessageQueue.cpp 和 Looper.cpp 对象,但他们并不都是与 Java 层一一对应的,在源码分析的过程中得知,只有MessageQueue.java 和 MessageQueue.cpp 有关联。在java层计算好延迟时间后调用 native 层 nativePollOnce 方法,其内部实现采用 epoll 来处理延迟等待返回(6.0版本)。当有新的消息插入时会调用 native 层的 nativeWake 方法,这个方法很简单就是向文件描述符中写入一个最简单的 int 数据 1,目的是为了唤醒之前的 epoll_wait 方法,其实也就是唤醒 nativePollOnce 的等待。

  1. 接下来我们看看 IdleHandler:这个是安卓的一个 空闲机制,也就是说,没有消息需要立即处理时,就会触发这个机制;
    IdleHandler是一个回调接口,可以通过MessageQueue的addIdleHandler添加实现类。当MessageQueue中的任务暂时处理完了(没有新任务或者下一个任务延时在之后),这个时候会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调。
    (1)使用:
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
  @Override
  public boolean queueIdle() {
    // 开始执行一些其它操作,可能不耗时也可能稍微耗时
    // 比如跨进程访问,比如查询数据库,比如收集某些信息,比如写日志等等
    return false;
  }
});

(2)源码:framework/base/core/java/android/os/MessageQueue.java

public void addIdleHandler(@NonNull IdleHandler handler) {
  if (handler == null) {
    throw new NullPointerException("Can't add a null IdleHandler");
  }
  synchronized (this) {
    mIdleHandlers.add(handler);
  }
}

Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration

        for (;;) {
            synchronized (this) {
                //  目前这里分析有内容
                if (msg != null) {
                        //如果有消息需要处理,就返回msg进行处理;
                        return msg;
                    }
                } else {
                    // No more messages.否则继续向下执行
                    nextPollTimeoutMillis = -1;
                }
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // 循环遍历所有的 IdleHandler 回调
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
                // 回调执行 queueIdle 方法
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                // 执行完函数返回是不是需要空闲时一直回调
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }
  1. 屏障消息:在平时开发中,绝大多数使用的消息都是普通消息,实际上 Handler 的消息可不知这一种(普通消息、异步消息、屏障消息),下面我们就来了解一下这三种消息;

(1)普通消息:最常用,所以就一笔带过了;

Message message = Message.obtain();
message.what = ***;
mHandler.sendMessageDelayed(message, 1000);

(2)异步消息:有两种方式创建,一种是通过Handler,另外一种是通过Message;

    // 在创建Handler时,传入的async参数是true
    public Handler(Callback callback, boolean async) {
        // 省略部分代码...
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    // 发送的 Message 设置 setAsynchronous(true)
    Message message = Message.obtain();
    message.what = XXX;
    message.setAsynchronous(true);
    mHandler.sendMessageDelayed(message, 1000);

(3)屏障消息:通过反射的方式调用。特征是target为null,不需要做分发处理。用于拦截队列中msg.when之后的同步消息,放行异步消息。在移除对应msg.arg1的屏障消息后,同步消息可恢复执行。

//往消息队列插入同步屏障
    @RequiresApi(api = Build.VERSION_CODES.M)
    public void sendSyncBarrier(){
        try {
            Log.d("TAG", "插入同步屏障");
            MessageQueue queue = handler.getLooper().getQueue();
            Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
            method.setAccessible(true);
            token= (int) method.invoke(queue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //移除屏障
    @RequiresApi(api = Build.VERSION_CODES.M)
    public void removeSyncBarrier(){
        try {
            Log.d("TAG", "移除屏障");
            MessageQueue queue = handler.getLooper().getQueue(); //或则Looper.myQueue()
            Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier",int.class);
            method.setAccessible(true);
            method.invoke(queue, token);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

(4)屏障消息源码:
插入屏障消息:

MessageQueue#postSyncBarrier
 private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            // 屏障消息没有tartget。
            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;
            }
            // 返回一个int的tocken,可以通过tocken撤销屏障
            return token;
        }
    }

postSyncBarrier方法就是用来插入一个屏障到消息队列的,可以看到它很简单,从这个方法我们可以知道如下:

工作原理
postSyncBarrier方法屏障就被插入到消息队列中了,那么屏障是如何挡住普通消息只允许异步消息通过的?我们重新看看MessageQueue.next()方法来分析一下:

Message next() {
    // ...
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //1. 如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
        nativePollOnce(ptr, nextPollTimeoutMillis);
 
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            // 还记得上面提到的屏障消息没有target吗?
            // msg.target == null,则msg为屏障消息,否则队列中的异步消息和同步消息无异
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                    // 跳过同步消息:当遍历到消息队列末尾,或者msg是异步消息,则退出循环
                } 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);
                } 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;
                }
            } else {
                // 如果没有异步消息就一直休眠,等待被唤醒。
                nextPollTimeoutMillis = -1;
            } 
            // ...
    }
}

移除屏障
MessageQueue的removeSyncBarrier方法:

public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            //找到token对应的屏障
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            final boolean needWake;
            //从消息链表中移除
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            //回收这个Message到对象池中。
            p.recycleUnchecked();
            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);//唤醒消息队列
            }
    }

Handler的源码分析就到这里了;

上一篇下一篇

猜你喜欢

热点阅读