Android 彻底掌握 Handler 看这里就够了(下)

简介: 重点关注Handler 的 post(Runnable) 与 sendMessage(Message msg) 有什么区别Handler.post()Handler.getPostMessage()Handler.sendMessage()Handrle.dispatchMessage()Handrle.handleCallback()Looper.loop() 为什么不会阻塞主线程MessageQueue.next()MessageQueue.nativePollOnce()android_os_MessageQueue_nativePollOnce

重点关注


Handler 的 post(Runnable) 与 sendMessage(Message msg) 有什么区别


样例:


public class HandlerActivity extends ActivityBase{
    ActivityHandlerBinding binding ;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 1:
                    binding.tvTotle.setText(String.format("哈哈哈哈考了%d",msg.arg1));
                    break;
            }
            return false;
        }
    });
    @Override
    protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityHandlerBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.btnSendMessage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = new Message();
                msg.what=1;
                msg.arg1=100;
                handler.sendMessage(msg);
            }
        });
        binding.btnPost.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        binding.tvTotle.setText(String.format("哈哈哈哈考了%d",80));
                    }
                });
            }
        });
    }
}


Handler.post()


public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }


       然后调用 getPostMessage() 传入 Runnable ,咱看看这个是干嘛的?


Handler.getPostMessage()


private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }


       在这个方法中你会发现getPostMessage()会将 Runnable 赋值到 Message 的 callback 变量中,返回一个Message。并调用 sendMessageDelayed 方法。


Handler.sendMessage()


 

public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }


       对比发现他们都调用 sendMessageDelayed() ,只不过生成的Message方式不同。 sendMessageDelayed() 上文已经讲到了就不多描述了。可以向上翻翻。


       可以看出,经过几层调用之后,sendMessageDelayed() 最终会调用 enqueueMessage() 方法将 Message 插入到消息队列 MessageQueue 中。而这个消息队列就是我们刚才分析的在 ActivityThread 的 main 方法中通过 Looper 创建的 MessageQueue。


       Looper 通过 loop() 方法从 MessageQueue 中取出 Message ,Message.target(Handrle) 会调用 dispatchMessage 方法进行处理。下面咱再看看它的源码。


Handrle.dispatchMessage()


    /**
     * Handle system messages here.
     * 在这里处理系统消息。
     */
    public void dispatchMessage(@NonNull Message msg) {
        //注释1
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        //注释2
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


 咱先看看handleCallback方法


Handrle.handleCallback()


    private static void handleCallback(Message message) {
        message.callback.run();
    }


很明显,dispatchMessage 分两种情况:

 注释1msg.callback != null,一般为通过 post(Runnabl) 方式,会直接执行 Runnable 的 run 方法。因此这里的 Runnable 实际上就是一个回调接口,跟线程 Thread 没有任何关系。

 注释2msg.callback == null,这种一般为 sendMessage 的方式,则会调用 Handler 的 hanlerMessage 方法进行处理。


Looper.loop() 为什么不会阻塞主线程


       刚才我们了解了,Looper 中的 loop 方法实际上是一个死循环。但是我们的 UI 线程却并没有被阻塞,反而还能够进行各种手势操作,这是为什么呢?在 MessageQueue 的 next 方法中.


MessageQueue.next()


    Message next() {
        // mPtr :used by native code
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        //-1:仅在第一次迭代期间
        int pendingIdleHandlerCount = -1; 
        //下一个Poll超时时间
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                //将当前线程中挂起的所有 Binder 命令刷新到内核驱动程序。 
                //在执行可能会阻塞很长时间的操作之前调用这会很有用,
                //以确保任何挂起的对象引用都已被释放,以防止进程持有对象的时间超过它所需的时间。
                Binder.flushPendingCommands();
            }
            //注释:重点来了。
            nativePollOnce(ptr, nextPollTimeoutMillis);
            ...
        }
    }


MessageQueue.nativePollOnce()


 

/*non-static for callbacks*/
    private native void nativePollOnce(long ptr, int timeoutMillis);


       nativePollOnce 方法是一个 native 方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制。下面是关于 nativePollOnce 的部分分析,参考了nativePollOnce函数分析,感兴趣的可以去自己看看,有点懵。等我再努力努力再来看这些吧。


epoll机制:提供了Linux平台上最高效的I/O复用机制。 从调用方法上看,epoll的用法和select/poll非常类似,其主要作用就是I/O复用,即在一个地方等待多个文件句柄的I/O事件。


nativePollOnce的实现函数是android_os_MessageQueue_nativePollOnce。


android_os_MessageQueue_nativePollOnce


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


pollOnce()


       frameworks/base/core/jni/android_os_MessageQueue.cpp


void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    //重任传递到Looper的pollOnce函数
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;
    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}1.


pollOnce()


       system/core/libutils/include/utils/Looper.h


/**
     * 等待事件可用,以毫秒为单位可选超时。
     * 为发生事件的所有文件描述符调用回调。
     * 如果超时为零,则立即返回而不阻塞。
     * 如果超时为负,则无限期等待直到事件出现。
     * 如果之前使用wake() 唤醒轮询,则返回POLL_WAKE
     * 超时到期,没有调用回调,也没有其他文件
     * 描述符已准备就绪。
     * 如果调用了一个或多个回调,则返回 POLL_CALLBACK。
     * 如果在给定之前没有数据等待超时,则返回 POLL_TIMEOUT。
     * 如果等待过程中发生错误,则返回 POLL_ERROR。
     * 如果文件描述符有数据,则返回一个 >= 0 包含标识符的值
     * 并且它没有回调函数(这里需要调用者来处理它)。
     * 在这个(并且只有这个)情况下,outFd、outEvents 和 outData 将包含投票
     * 与 fd 关联的事件和数据,否则它们将被设置为 NULL。
     *
     * 此方法在完成调用适当的回调之前不会返回
     * 用于所有已发出信号的文件描述符。
     */
    int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
    inline int pollOnce(int timeoutMillis) {
        //timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。如果值为0,则无需等待立即返回。
        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
    }


pollOnce()


       system/core/libutils/Looper.cpp


1.int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    //一个无限循环
    for (;;) {
        //mResponses是一个Vector,这里首先需要处理response
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident;
            if (ident >= 0) {
                //首先处理那些没有callback的Response
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
                ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
                        "fd=%d, events=0x%x, data=%p",
                        this, ident, fd, events, data);
#endif
                if (outFd != nullptr) *outFd = fd;
                if (outEvents != nullptr) *outEvents = events;
                if (outData != nullptr) *outData = data;
                //实际上,对于没有callback的Response,pollOnce只是返回它的ident,
                //并没有实际做什么处理。因为没有callback,所以系统也不知道如何处理
                return ident;
            }
        }
        if (result != 0) {
#if DEBUG_POLL_AND_WAKE
            ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
            if (outFd != nullptr) *outFd = 0;
            if (outEvents != nullptr) *outEvents = 0;
            if (outData != nullptr) *outData = nullptr;
            return result;
        }
        //调用pollInner函数。注意,它在for循环内部
        result = pollInner(timeoutMillis);
    }
}



Handler 的 sendMessageDelayed 或者 postDelayed 是如何实现的


       上面说到,在向 MessageQueue 队列中插入 Message 时,会根据 Message 的执行时间排序。而消息的延时处理的核心实现是在获取 Message 的阶段,接下来看下 MessageQueue 的 next 方法。


MessageQueue.next()


Message next() {
        ...
        for (;;) {
            ...
            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:指示 next() 是否在 pollOnce() 中以非零超时阻塞等待。
                        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();
                        //返回 Message 
                        return msg;
                    }
                } else {
                    // 没有更多的消息。
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }


上面表示从 MessageQueue 中取出一个 Message,但是当前的系统时间小于 Message.when,因此会计算一个 timeout,目的是实现在 timeout 时间段后再将 UI 线程唤醒,因此后续处理 Message 的代码只会在 timeout 时间之后才会被 CPU 执行。


注意:在上述代码中也能看出,如果当前系统时间大于或等于 Message.when,那么会返回 Message 给 Looper.loop()。但是这个逻辑只能保证在 when 之前消息不被处理,不能够保证一定在 when 时被处理。


总结


微信图片_20220522212155.png

相关文章
|
1月前
|
消息中间件 网络协议 Java
Android 开发中实现数据传递:广播和Handler
Android 开发中实现数据传递:广播和Handler
27 1
|
1月前
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
【Android开发小技巧】扔掉这坑人的 Handler
46 0
|
19天前
|
Android开发
38. 【Android教程】Handler 消息传递机制
38. 【Android教程】Handler 消息传递机制
13 2
|
1月前
|
消息中间件 安全 数据处理
Android之Handler、Message、MessageQueue、Looper详解2
Android之Handler、Message、MessageQueue、Looper详解
39 0
|
1月前
|
Java Android开发
Android之Handler、Message、MessageQueue、Looper详解1
Android之Handler、Message、MessageQueue、Looper详解
32 0
|
10月前
|
Android开发
Android面试常客之Handler全解1
Android面试常客之Handler全解
|
1月前
|
消息中间件 缓存 安全
android开发,使用kotlin学习消息机制Handler
android开发,使用kotlin学习消息机制Handler
147 0
|
10月前
|
XML 消息中间件 API
Android 中handler消息机制的理解
Android 中handler消息机制的理解
59 0
|
10月前
|
XML Android开发 数据格式
Android 中简单计时器的实现方法(Handler和TimerTask)
Android 中简单计时器的实现方法(Handler和TimerTask)
373 0
|
10月前
|
消息中间件 Android开发
Android面试常客之Handler全解2
Android面试常客之Handler全解