《Android内核剖析》笔记 第5章 进程间通信核心框架Binder

来源:互联网 发布:sql select 创建表 编辑:程序博客网 时间:2024/05/20 04:47

 Binder:英文的意思是别针、回形针。我们经常用别针把两张纸“别”在一起,在android系统中,Binder是用来完成进程间通信IPC的基础框架,即把不同进程“别”在一起,便于各个进程之间可以相互传递消息;如果做过Java服务端开发的话,你也可以理解成一种RPC调用,即在本地直接调用其他进程中的功能;理解该机制将有助于更好的掌握后面的android整体架构设计,因为核心模块之间的通信都是通过Binder来完成的,这也是为什么作者将这部分内容放在内核篇的第一章的原因;

Binder框架由三部分组成,分别为服务端、Binder驱动、客户端,详见下图:
alt

  1. 服务端:即服务提供者,比如android就提供了很多的系统服务,比如Alarm、WIFI、INPUT、LAYOUT_INFLATER、ACTIVITY等;当然开发者也可以实现自己的服务,然后把服务开放给其他应用使用,在代码上服务接口必须继承 IInterface,服务具体实现类必须继承 Binder
  2. Binder驱动:实际上他是一个遵从Linux设备驱动模型的虚拟驱动,设备节点为/dev/binder;他主要用来实现客户端和服务端请求的中转,其相关源码位于./frameworks/native/libs/binder/目录;
    具体驱动的打开、监听是通过守护进程servicemanager完成,在./init.rc中定义,相关源码位于./frameworks/base/cmds/servicemanager/;
  3. 客户端:即服务消费者,要想调用其他进程中的功能,必须在本地有一个对远程对象的引用,且客户端和服务端必须遵从相同的接口和数据交换协议;而这两点就是实现IPC通信需要解决的最核心的两个问题;

为了简化服务提供者和消费者之间的开发和沟通成本,android系统提供了AIDL(Android Interface Definition Language)的工具支持,该工具可以把一个用于接口声明的aidl文件转换成一个Java文件,转换的过程就是要让该服务在代码层面自动符合Binder框架的设计规范,并自动解决好上面提到的2个核心问题中的第二点,生成好的Java文件其实包括了3个类:

服务接口类IXXX extends android.os.IInterface;

本地存根类IXXX.Stub  extends android.os.Binder implements IXXX;

远程代理类 IXXX.Stub.Proxy implements IXXX;

经历过EJB时代的同学们一定对上面的代码形式相当熟悉吧,哈哈!

以上文件生成好之后,服务提供者就可以专注于写具体的逻辑代码了,直接extends IXXX.Stub 实现具体的方法即可;

那是否可以不用aidl工具来生成代码呢?当然可以,如果你愿意的话完全可以自己来写相关的代码,只要符合Binder代码规范即可;

 

服务提供者使用的本地存根类IXXX.Stub中的核心方法

  1. public final boolean transact(int code, Parcel data, Parcel reply, int flags);
    实际上是父类Binder中的方法,因为是final的,所以子类是不能重载的,这也是Binder框架设计的一部分,因为他不允许开发者随意改变调用机制,该方法用于接受服务调用请求,但他自己并不直接处理这些请求,而是转交给onTransact方法(可由子类重载)来处理,这是android系统中非常重要的一种设计思想,被广泛用于各种场景,以后你看见onXXXX相关的方法一般都是相关的功能回调或子类重载点;
  2. protected boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags); 
    根据参数code判断该执行IXXX接口中的哪个具体业务方法,并且从data中按序取出数据作为具体业务方法的输入参数;
    注意:从Parcel取出数据的顺序必须和存入数据(具体间后面IXXX.Stub.Proxy中业务方法的描述)的顺序一致,否则数据就乱套了,而这就是AIDL工具自动生成代码要解决的核心问题;
  3. public static IXXX asInterface(android.os.IBinder obj);
    将远程对象的引用转换成具体的业务接口,这里自动屏蔽了本地和远程调用的细节,因为服务提供方不仅可以被其他进程调用,还可以被自身进程内其他功能块调用;

服务消费者使用的远程代理类 IXXX.Stub.Proxy中的核心方法

  1. Proxy(android.os.IBinder remote);
    构造函数,参数就是远程服务的引用,所有的业务方法的调用都是通过该引用间接执行的;
  2. xyz(…) 各种具体的业务方法;
    这里对服务接口的实现并不是真的业务逻辑实现,他是远程服务在使用方本地的代理,用来中转调用的,主要是完成输入参数到包裹Parcel的转换,因为IPC远程调用对消息的传递必须基于包裹Parcel,这是Binder框架设计规范的一部分;
    当输入参数转换完成后调用 mRemote.transact(…)将请求发送给服务提供方;

服务消费者如何获取远程对象的引用?

  1. 对于系统服务(系统启动时在SystemServer进程中自动加载),android提供了统一的获取接口,使用了工厂模式,可以通过ServiceManager.getService(.)得到远程对象的引用IBinder,然后通过IXXX.Stub.asInterface(.)方法即可得到对具体服务接口IXXX的引用;
    注意:我们一般不会直接使用到服务接口IXXX,而是会用到对应的Manager类(可以理解为远程服务对象的经纪人),这也是android系统的一种常见设计规范;这种设计的作用是屏蔽对远程服务的直接访问,从而给应用程序提供灵活可控的API接口;
    也不会直接使用到
    ServiceManager,而是直接通过context.getSystemService(.)获取到具体服务对应的Manager类;
    关于具体服务Service和Manager之间的关系可以参见下图:
    alt
  2. 对于非系统服务,可以通过执行context.startService(.)来启动,执行bindService(Intent service, ServiceConnection conn, int flags)来获取服务的远程对象引用,具体是在ServiceConnection.onServiceConnected(ComponentName name, IBinder service);这个回调函数中获得;可参见下图:
    alt
原创粉丝点击