返回目录:金融新闻
一、Binder 类比 TCP/IP
我们知道,同一个程序中的两个函数之间能直接调用的根本原因是出于相同的内存空间中。反之,两个不同的进程,它们是没有办法直接通过内存地址来访问到对方内部的函数或者变量的,那有没有“间接”的方法呢?这就是Binder所要做的工作。
Binder是Android中使用的最广泛的IPC机制。Binder通信的组成元素有如下几个:
Binder驱动
如果你熟悉TCP/IP网络的话,你会发现这两者有很多相似之处,
Binder驱动 -- 路由器RouterService Manager -- NDSBinder Client -- 客户端Binder Server -- 服务端12341234
TCP/IP中一个典型的服务连接过程如下:
1)Client向DNS查询Google.com的IP地址
显然,Client一定得先知道DNS的IP地址,才有可能向它发起查询。DNS的IP设置是在客户端接入网络前就完成了的。当然,如果Client已经知晓了Server的IP,那么完全可以跨越这一步而直接与Server连接。比如Windows操作系统就提供了一个hosts文件,用于查询常用网址域名与其IP地址的对应关系。当用户需要访问某个网址时,系统会先从这个文件中判断是否已经存有这个域名的对应IP。如果有就不需要再大费周折地向DNS查询了,从而加快访问速度。
2)DNS将查询结果返回Client
3)Client发起连接
在这个过程中,我们可以知道:
1)IP地址是他们彼此沟通的凭证;
2)上述过程并没有特别提及Router的作用,因为Router是构建一个通信网络的基础,它可以根据用户填写
的目标IP正确地把数据包发送到位。
3)NDS角色并不是必须的,它的出现是为了帮助人们使复杂难记的IP地址与可读性更强的域名建立关联,并提供查询功能。而客户端能使用DNS的前提是它已经配置了DNS服务器的IP地址。
清楚了网络通信中各个功能模块所扮演的角色,我们将它和Binder做一个对比。
进程1(客户端)与进程2(服务端)进行通信,因为它们之间是跨进程的,
既然Service Manager是DNS,那么它的“IP地址”是什么呢?Binder机制对此做了特别规定:
Service Manager在Binder通信过程中的唯一标志永远都是0
二、Binder通信过程(transact和onTransact)
任何service在被使用之前,均要向SM(Service Manager)注册。
SM的主要工作是初始化 Binder ,打开 /dev/binder 设备;在内存中为 Binder 映射 128K 字节空间。
里面维护一个死循环,在这个死循环中,不停地去读内核中 Binder Driver ,查看是否有可读的内容,即是否有对 service 的操作请求 , 如果有,则调用 svcmgr_handler 回调来处理请求的操作。
客户端和 服务端之间的通信会涉及到 2 次Binder 通信。
1)客户端向 SM 查询 service 是否存在,如果存在获得该 service 的代理 Binder (唯一标识),此为一次 Binder 通信;
2)客户端通过代理 binder 调用 service 的方法,此为第二次 Binder 通信。
比如我要和AMS通信,先得到AMS服务对象的唯一标志:
IBinder b = ServiceManager.getService(“activity”);11
返回一个IBinder对象,这个对象就是上面我们所讲的Binder颁发的唯一标识(IP地址)。
IBinder是一个接口,里面主要API是transact(),
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;1212
客户端拿到服务端的唯一标志IBinder对象,即可通过这个对象的transact方法向服务端发起请求。
第一个参数表示这一次请求执行的意图,
服务端要支持远程调用,应从Binder类派生一个类。Binder类继承自IBinder,Binder类里有一个onTransact()方法。服务端通过这个方法响应客户端的请求。
三、AIDL通信原理
AIDL通信是对Binder通信的封装。因为要实现IBinder来支持远程调用,应从Binder类派生一个类。而使用AIDL这个工具,它会自动生成Binder的派生类,可以直接用。
比如定义一个AIDL文件:
package com.example.aidl;interface AB {
编译器会自动生成AB.Java文件,这个文件是不让被修改的:
public interface AB extends android.os.IInterface{
在这个Java文件中主要有两个内部类,分别表示客户端和服务端:
表示客户端的内部类(嵌套在服务端内)Proxy :客户端调用a()方法,我们可以看到在a方法里通过IBinder对象向服务端发起请求:
mRemote.transact(Stub.TRANSACTION_a, _data, _reply, 0);
第一个参数: 看看code的构造。
static final int TRANSACTION_a = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
第四个参数0表示transact方法默认的是同步阻塞的。
可以在aidl接口的方法前加 oneway,如:
oneway void a(int i);
表示此方法单向调用,执行后立即返回,无需等待服务端返回。
这时生成的java文件中的transact方法的调用为如下:
mRemote.transact(Stub.TRANSACTION_a, _data, null, IBinder.FLAG_ONEWAY);
private static class Proxy implements AB{
表示服务端的内部类 Stub :
onTransact方法响应请求,通过code,执行了a()方法。可以想到,在服务端要实现这个方法。
asInterface(IBinder obj)方法可以返回客户端对象(代理)。
asBinder()方法返回IBinder对象。
public static abstract class Stub extends Binder implements AB{
所以我们使用AIDL通信时,需要在客户端和服务端定义同一个AIDL文件,并生成同一个Java文件。
所以需要注意的一点是客户端和服务端的AIDL文件包名要一致。
四、AIDL通信例子
客户端:
1、aidl文件:
package com.example.aidl;interface AB {
2、ServiceConnection :
ServiceConnection conn = new ServiceConnection() { @Override
3、绑定服务:
Intent in = new Intent();in.setPackage("com.example.server");in.setAction("AB");bindService(in, conn, Context.BIND_AUTO_CREATE);12341234
服务端: 与客户端的aidl文件包名一致
1、aidl文件:
package com.example.aidl;interface AB {
2、定义一个Service:
<service android:name=".MyService" android:exported="true">
3、实现aidl类
AB ab = new AB.Stub() {
4、响应服务的绑定
@Overridepublic IBinder onBind(Intent intent) { return ab.asBinder();