简介
Binder使用Client-Server
通信方式。Binder框架定义了四个角色:Server
,Client
,ServiceManager
以及Binder驱动
。其中Server
,Client
,ServiceManager
运行于用户空间,驱动运行于内核空间。Binder驱动程序提供设备文件/dev/binder
与用户空间交互,Client
、Server
和Service Manager
通过open
和ioctl
文件操作函数与Binder驱动程序进行通信。
Binder原理简述
- Server创建了Binder实体,为其取一个字符形式,可读易记的名字。
- 将这个Binder连同名字以数据包的形式通过Binder驱动发送给
ServiceManager
,通知ServiceManager
注册一个名字为XX的Binder,它位于Server中。 - 驱动为这个穿过进程边界的Binder创建位于内核中的实体结点以及ServiceManager对实体的引用,将名字以及新建的引用打包给ServiceManager。
ServiceManager
收数据包后,从中取出名字和引用填入一张查找表中。但是一个Server若向ServiceManager注册自己Binder就必须通过这个引用和ServiceManager
的Binder通信。- Server向
ServiceManager
注册了Binder实体及其名字后,Client就可以通过名字获得该Binder的引用了。Clent也利用保留的引用向ServiceManager
请求访问某个Binder:我申请名字叫XX的Binder的引用。 ServiceManager
收到这个连接请求,从请求数据包里获得Binder的名字,在查找表里找到该名字对应的条目,从条目中取出Binder引用,将该引用作为回复发送给发起请求的Client。
当然,不是所有的Binder都需要注册给ServiceManager
广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向ServiceManager注册名字,所以是 匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。
Binder的数据拷贝
Linux内核实际上没有从一个用户空间到另一个用户空间直接拷贝的函数,需要先用copy_from_user()
拷贝到内核空间,再用copy_to_user()
拷贝到另一个用户空间。为了实现用户空间到用户空间的拷贝,mmap()
分配的内存除了映射进了接收方进程里,还映射进了内核空间。所以调用copy_from_user()
将数据拷贝进内核空间也相当于拷贝进了接收方的用户空间,这就是Binder只需一次拷贝的”秘密”。
最底层的是Android的ashmen(Anonymous shared memory)
机制,它负责辅助实现内存的分配,以及跨进程所需要的内存共享。AIDL(android interface definition language)对Binder的使用进行了封装,可以让开发者方便的进行方法的远程调用,后面会详细介绍。Intent是最高一层的抽象,方便开发者进行常用的跨进程调用。
使用共享内存通信的一个显而易见的好处是效率高,因为 进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次内存数据:一次从输入文件到共享内存区,另一次从共享内存到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域,而是保持共享区域,直到通信完成为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除内存映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
Window绘制过程
在理解Window
绘制过程之前,首先,我们需要知道Surface
,在Window
中持有一个Surface
,那么什么是Surface
呢?
Surface
其实就是一个持有像素点矩阵的对象,这个像素点矩阵是组成显示在屏幕的图像的一部分。我们看到显示的每个Window(包括对话框、全屏的Activity、状态栏等)都有他自己绘制的Surface
。而最终的显示可能存在Window
之间遮挡的问题,此时就是通过Surface Flinger对
象渲染最终的显示,使他们以正确的Z-order
显示出来。一般Surface
拥有一个或多个缓存(一般2个),通过双缓存来刷新,这样就可以一边绘制一边加新缓存。
WindowManager
为每个Window
创建Surface
对象,然后应用就可以通过这个Surface
来绘制任何它想要绘制的东西。而对于WindowManager
来说,这只不过是一块矩形区域而已。
前面我们说过,View
是Window
里面用于交互的UI元素。Window
只attach一个View Tree
,当Window
需要重绘(如,当View调用invalidate
)时,最终转为Window
的Surface
,Surface
被锁住(locked)并返回Canvas对象,此时View拿到Canvas对象来绘制自己。当所有View绘制完成后,Surface
解锁(unlock),并且post到绘制缓存用于绘制,通过Surface Flinger
来组织各个Window,显示最终的整个屏幕。
总结
现在我们知道了Window
绘制过程,其实,站在系统的角度来考虑,一个Window对象代表一块显示区域,系统不关心Window里面具体的绘制内容,也不管你Window
怎么去绘制,反正只给你提供可以在这块区域上绘制图形的Surface
对象,你Window
对象怎么画是你的事情!
换句话说,站在系统的角度上看,系统是“不知道”有View对象这个说法的!作为系统,我有自己的骄傲,不去管你Window如何搬砖、如何砌墙,只给你地皮。而这时,Window为了绘制出用户想要的组件(按钮、文字、输入框等等),系统又不给我!没事,那我自己定义,于是就定义了View机制,给每个View提供Canvas,让不同的View自己绘制具有自己特色的组件。同时,为了更好的管理View,通过定义ViewGroup,等等。
Activity
对于开发人员来说,一个Activity
就“相当于”一个界面(通过setContentView
指定具体的View)。我们可以直接在Activity里处理事件,如onKeyEvent
,onTouchEvent
等。 并可以通过Activity维护应用程序的生命周期。
Activity和Window
前面我们知道,Window
已经是系统管理的窗口界面。那么为什么还需要Activity
呢?我们把Activity
所做的事情,全部封装到Window
不就好了?
其实,本质上讲,我们要显示一个窗口出来,的确可以不需要Activity。悬浮窗口中不就是没有使用Activity来显示一个悬浮窗吗?既然如此,Window(以及View)能处理点击事件以及封装各种逻辑,那为啥还需要Activity呢?
Android
中的应用中,里面对各个窗口的管理相当复杂(任务栈、状态等等),Android系统当然可以不用Activity,让用户自己直接操作Window来开发自己的应用。但是如果让用户自己去管理这些Window,先不说工作量,光让用户自己去实现任务栈这点,有几个人能写的出来。为了让大家能简单、快速的开发应用,Android通过定义Activity,让Activity帮我们管理好,我们只需简单的去重写几个回调函数,无需直接与Window对象接触。各种事件也只需重写Activity里面的回调即可。无需关注其他细节,默认都帮我们写好了,针对需要定制的部分我们重写(设计模式为:模板方法模式)。