• Android Handler 机制(三):Handler 机制与管道 Pipe 机制


    在深入系统的学习Handler的时候,我们接触到了Looper之所以死循环不会导致CPU使用率过高,是因为使用了Linux下的pipe和epoll机制。

    Android的应用层通过Message.java实现队列,利用管道和epoll机制实现线程状态的管理,配合起来实现了Android主线程的消息队列模型。

    对Handler,我们在之前整理了如下内容,也上对Handler的机制有了相当程度的了解:

    Android Handler 机制(一):Handler 运行机制完整梳理 

    Android Handler 机制(二):Hander 机制深入探究问题梳理 

    同时为了更进一步的了解Handler机制,我们整理了Epoll机制的相关基本知识:

    Linux下Epoll机制概述

    本文我们讲述一下Android的Handler机制为何使用管道。

    一、管道概述

    管道,其本质是也是文件,但又和普通的文件会有所不同:管道缓冲区大小一般为1页,即4K字节。管道分为读端和写端,读端负责从管道拿数据,当数据为空时则阻塞;写端向管道写数据,当管道缓存区满时则阻塞。

    在Handler机制中,Looper.loop方法会不断循环处理Message,其中消息的获取是通过 Message msg = queue.next(); 方法获取下一条消息。该方法中会调用nativePollOnce()方法,这便是一个native方法,再通过JNI调用进入Native层,在Native层的代码中便采用了管道机制。

    二、Handler为何使用管道?

    我们可能会好奇,既然是同一个进程间的线程通信,为何需要管道呢?

    我们知道线程之间内存共享,通过Handler通信,消息池的内容并不需要从一个线程拷贝到另一个线程,因为两线程可使用的内存时同一个区域,都有权直接访问,当然也存在线程私有区域ThreadLocal(这里不涉及)。即然不需要拷贝内存,那管道是何作用呢?

    Handler机制中管道作用就是当一个线程A准备好Message,并放入消息池,这时需要通知另一个线程B去处理这个消息。线程A向管道的写端写入数据1(对于老的Android版本是写入字符`W`),管道有数据便会唤醒线程B去处理消息。管道主要工作是用于通知另一个线程的,这便是最核心的作用。
    这里我们通过两张图来展示Handler在Java层和在Native层的逻辑:
    Java层:

    Native层:

    三、Handler为何采用管道而非Binder?

    handler不采用Binder,并非binder完成不了这个功能,而是太浪费CPU和内存资源了。因为Binder采用C/S架构,一般用于不同进程间的通信。
    • 从内存角度:通信过程中Binder还涉及一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一个内存。Handler需要的仅仅是告诉另一个线程数据有了。
    • 从CPU角度,为了Binder通信底层驱动还需要为何一个binder线程池,每次通信涉及binder线程的创建和内存分配等比较浪费CPU资源。

    从上面的角度分析可得,Binder用于进程间通信,而Handler消息机制用于同进程的线程间通信,Handler不宜采用Binder。

    四、Android 6.0及以后的机制

    在Android 6.0及以前的版本使用管道与epoll来完成Looper的休眠与唤醒的。

    在Android 6.0及以后的新版本中使用的是eventfd与epoll来完成Looper的休眠与唤醒的。

    感兴趣的可以进一步的学习和了解管道的知识及eventfd的知识,并比较一下两种机制的优劣,进而明白Android官方为何对此机制进行调整。

  • 相关阅读:
    图形信息与文字信息的区别
    逻辑后承:从语句到图形
    面向计算机科学的非经典逻辑
    安装ubuntu10.10后,如何配置一个Apache+MySQL+PHP环境
    如何查看RPG程序从何处编译
    向远程系统提交命令
    如何查看未备份成功的文件列表
    如何显示查询的调试信息
    如何检查谁删除了文件
    如何在SQL/400查询指令结果的最后一行插入合计
  • 原文地址:https://www.cnblogs.com/renhui/p/12875396.html
Copyright © 2020-2023  润新知