IPC跨进程通讯的方式有 socket/管道/广播/binder,在android 中用的最广泛的就是binder几乎底层通讯都是使用binder来处理的例如:ActivityThread 和 IApplication等等,少数使用了socket 例如在zogit系统启动的时候。
目录
binder通讯原理
在手机系统中我们可以创建很多app,每一个app就是一个进程,每个进程/线程的内存都是独立的,不是数据共享的。
但是在系统内核中内存是共享的,所以我们要想实现夸进程就需要通过内核层的内存实现数据交互/共享。
内核中内存的数据共享就是通过binder驱动来实现的。
不同进程之间数据交互是通过内核层的‘内存共享’实现的,个人理解他的思想就和我们的 多线程开发 道理很相似:多个线程之间是不能直接交互数据的,每个线程都有自己的工作内存,每个线程工作完后就会把数据拷贝到主内存中,然后其他线程才能获取到新数据。
binder在android系统中分为三层:
1.java层就是我们的Application层。
2.jni层过度层,帮助我们java层和c/c++层传递数据
3.c/c++层 binder 驱动层 核心层
我们重点需要了解的有java层 和 binder驱动层,java层也就是我们直接使用的层面主要有 IBinder 接口 和 Binder类以及IInterface接口,Binder实现了 IBinder 接口,有些重要的方法,接下来在做分析。
binder驱动层有binder.c 和 binder.h 文件,binder在底层有很多 结构体比如binder_node等等。
我们应用层每创建一个service,底层就对应创建一个binder实体,binder实体链接了我们的serve端和client端,并且根据具体的discriptor去通知serviceManager去返回对应的service对象给client使用。
代码调用流程
- service的注册
我们需要注意的是两个接口和一个类1.IInterface 2. IBinder 3. Binder
1. IInterface/** * Base class for Binder interfaces. When defining a new interface, * you must derive it from IInterface. 翻译:必须要实现的接口,当创建binder子类时候 */ public interface IInterface { /** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */ public IBinder asBinder(); }
通过注释我们可以看到 我们创建binder子类时候,必须要实现的接口。所以:创建一个接口IClient
public interface IClient extends android.os.IInterface { public void take(com.parrot.car.aidl.Book x) throws android.os.RemoteException; /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.parrot.car.aidl.IClient {
绑定当前binder对象到IInterface接口
/** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); }
2. IBinder 接口有一个重要的方法
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException;
3. Binder 类
BInder类中有一个内部类BinderProxy这个内部类主要是链接Jni层的代理类
binder中实现的Ibinder接口的方法/** * Default implementation rewinds the parcels and calls onTransact. On * the remote side, transact calls into the binder to do the IPC. */ public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { if (false) Log.v("Binder", "Transact: " + code + " to " + this); Log.e("---bbb-----","----transact----"); if (data != null) { data.setDataPosition(0); } boolean r = onTransact(code, data, reply, flags); if (reply != null) { reply.setDataPosition(0); } return r; }
这个方法是被我们客户端直接调用的。
// Entry point from android_util_Binder.cpp's onTransact //翻译:被jni 层的 android_util_Binder.cpp 的 onTransact 方法调用 private boolean execTransact(int code, long dataObj, long replyObj, int flags) { BinderCallsStats binderCallsStats = BinderCallsStats.getInstance(); BinderCallsStats.CallSession callSession = .... res = onTransact(code, data, reply, flags); } catch (RemoteException|RuntimeException e) { ... return res; }
这个方法是被系统底层jni层调用的,application层不直接调用
关于BinderProxy是底层的代理类,直接访问的是JNI层/** * Java proxy for a native IBinder object. * Allocated and constructed by the native javaObjectforIBinder function. Never allocated * directly from Java code. */ final class BinderProxy implements IBinder {
上面是java层重要的类,整个通讯的开始是从serve端向serviceManager注册service对象,首先serve端的Stub构造方法中会根据这个DISCRIPPOR字段把当前的service绑定到IInterface上。service对象会被注册到serviceManager中根据,在native层service是以binder实体存在,每个service在binder驱动中都有一个binder实体。
- client获取service对象
客户端通过binderService()调用/** * Cast an IBinder object into an com.parrot.car.aidl.IClient interface, * generating a proxy if needed. */ public static com.parrot.car.aidl.IClient asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.parrot.car.aidl.IClient))) { return ((com.parrot.car.aidl.IClient) iin); } return new com.parrot.car.aidl.IClient.Stub.Proxy(obj); }
会根据这ESCRIPTOR到serviceManager中获取service对象。
- 调用方法传递数据
binder优点
1.binder的数据传输的时候只需要拷贝一次,而其他通讯需要拷贝两次,所以极大的降低了底层的耗时。
2.binder的安全性,因为binder在通讯时系统会先通过uid和pid的验证,然后才通讯。
3.binder是使用C/S的通讯,client和serve通讯简单方便,而且binder是liunx自带的通讯方式。
Binder 实例: https://github.com/WangRain1/aidl
参考学习: https://www.cnblogs.com/xinmengwuheng/p/7070167.html
https://www.cnblogs.com/mingfeng002/p/10696023.html