欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android跨进程之Binder机制概述

程序员文章站 2022-05-25 17:17:09
binder(机制) 首先binder是一个类,实现了ibinder接口,其次从ipc角度来看,就是android中跨进程通信机制。 为什么使用binder作为跨进程机制? linux中使用管道(p...

binder(机制)

首先binder是一个类,实现了ibinder接口,其次从ipc角度来看,就是android中跨进程通信机制。

为什么使用binder作为跨进程机制?

linux中使用管道(pipe)做为进程之间的通信方式,虽然android内核也是使用的linux,但是作为移动端,性能和内存角度考虑,使用binder是最好的方式。管道和socket数据拷贝的次数为2次,文件共享0次,但是其安全性低太多,binder数据拷贝次数为1次,也能为发送端提供身份验证。

Android跨进程之Binder机制概述

这是罗升阳博客androidbinder机制中的四个client、server、service manager和binder驱动程序中的关系图

binder机制涉及到最重要的四个组件client、server、service manager和binder驱动设备

client和server都是在应用层实现的,server、client都是运行在独立的进程中,两者之间的通信就是ipc的方式,其中就要通过service manager和binder驱动设备。

client:客户端(调用iservicemanager::getservice和binder驱动程序交互) server: 服务端(调用iservicemanager::addserie和binder驱动程序交互) service manager:实际也是server,更是一种守护线程,管理server,并向client提供查询server接口 binder驱动:搭建进程之间通信的建立,并且支持data数据包的传输

举个栗子 这里使用mediaservice

mediaservice

在native层可以找到mediaservice的:

int main(int argc, char** argv)

{

//获得一个processstate实例

sp proc(processstate::self());

//得到一个servicemanager对象

    sp sm = defaultservicemanager();

    mediaplayerservice::instantiate();//初始化mediaplayerservice服务

    processstate::self()->startthreadpool();//看名字,启动process的线程池?

    ipcthreadstate::self()->jointhreadpool();//将自己加入到刚才的线程池?

}

最重要的servicemanager是通过defaultservicemanager()获取到的,中间还要获取servicemanager的binder代理,实在太复杂,不想多说,然后初始化mediaplayerservice服务,也是打开binder驱动设备,设置looper循环。

servicemanager存在的意义:

server先add到servicemanager中几种管理,client想要和server交互,需要先到servicemanager中查询service信息,然后通过sm返回的东西和server交互

在servicemanager中其源码service_manager知道了三点作用:

打开了binder设备 告诉binder驱动设备,自己就是binder的上下文管理者,也就是守护线程 进入一个无线循环,充当server的角色

binder的使用场景:

1 activity启动

activity的启动需要用到activitymanagerservice,但是我们的app进程和activitymanagerservice所在的进程不是同一个进程,所以就需要用到进程间通讯了。在app进程中我们拿到的是activitymanagerservice的一个分身,也就是activitymanagerproxy,这个activitymanagerproxy与activitymanagerservice都实现了iactivitymanager接口,因此它们具有相同的功能,但是activitymanagerproxy只是做了一个中转,创建两个parcel对象,一个用于携带请求的参数,一个用于拿到请求结果,然后调用transact方法,通过binder驱动,activitymanagerservice的ontransact方法会被调用,然后根据相应的code,调用相应的方法,并把处理结果返回。

在这个过程中,我们的app进程就是client,activitymanagerservice所在的进程是service

但是activity的启动过程还没有完,activitymanagerservice还会调用我们app所在进程的applicationthread来最终完成activity的启动,其实activitymanagerservice拿到的也是applicationthread的一个分身applicationthreadproxy,通过这个分身,applicationthread相应的方法会被调用。

在这个过程中,我们的app端是server,activitymanagerservice所在的进程是client。

还有一个问题我们要注意,activitythread有一个内部类h(一个hander),applicationthread方法内部都会通过这个handler来发送消息,最终调用到activitythread的方法。为什么要这么做呢?

在分析源码的过程中,很长一段时间,这个问题都困扰着我,直到有一天对binder的理解加深了,我才明白:binder服务端的方法都是运行在binder线程池的一个线程中的,所以要通过hander,把方法的调用切换到主线程中来。

2 intent传递数据

我们都知道intent可以传递的数据包含:基本类型、string、实现了serializable接口或者parcelable接口的类以及对应的数组或者集合类。其实intent中的数据都是通过bundle来携带的,那么我们就要有个疑问了,为什么限定只能是这些类型的数据,而不是任意的数据类型呢?

归根结底,限制这些类型的是parcel这个类。如果我们查看源码的话就会看到,bundle其实也是用到了parcel这个类。

parcel
,“包裹的意思”,它的作用就是为了在ipc过程中存放数据。我们要知道一点,进程间传递数据,实际上就是二进制数据,所以对于非基本类型,必然存在着序列化和反序列过程,这也是为什么要求intent传递的非基本类型数据必须实现serializable或者parcelable接口的原因。

至于parcel在ipc过程中使用到的地方,我们可以看一段代码,这个是我仿造着aidl生成的文件,自己手写的一个binder服务端。看一下proxy类的add方法,实际上就是先创建两个parcel对象,一个通过调用
writexxx 方法用于存放请求数据,一个是通过调用 readxxx
方法获取结果。proxy真正干的就是这些,真正计算的还是服务端stub的实现类。当proxy调用
mremote.transact(transaction_add, _data, _reply, 0);
方法后,stub的ontransact方法会被调用,进而调用真正的add方法。