04. Android Binder图解 小米系统专家 解析binder机制 java层(安卓12)

很多BAT也不一定能懂的binder机制!

因为搞懂binder需要会c,linux内核知识。看java根本就看不懂!

我同事从小米跳槽过来,干安卓framework层10年,是小米的专家级别

然后他把binder驱动层全部和我讲解了一遍,然后我这边做个笔记分享给大家。

分6篇文字讲解:

01. Android Binder图解 小米系统专家 解析Service 的addService注册过程 (安卓12)

02. Android Binder图解 小米系统专家 解析 ServiceManager和binder通信 (安卓12)

03. Android Binder图解 小米系统专家 解析binder驱动层解析binder通信过程 (安卓12)

04. Android Binder图解 小米系统专家 从binder java层解析binder整个流程(安卓12)

05. Android Binder图解 小米系统专家 解析binder总结调用流程 (安卓12)

06. Android Binder图解 小米系统专家 解析binder面试一网打尽(安卓12)

db2f290b-70e5-4a5e-8006-31a85cb84e75.jpg

问题引申:跨进程几种方式----优点-----mmkv原理-----1次内存拷贝-----跨进程过程-----内存映射---序列化---应用场景

最重要的3个问题

1.从java出发的,整个通信流程

2.内存映射和一次拷贝的流程

3.代理模式 和AIDL的原理

4.其他跨进程方式

普通跨进程二次拷贝的过程:

[图片上传失败...(image-becf5d-1642817053966)]

数据从用户空间拷贝到内核空间:第一次拷贝:copy_from_user()

数据从内核空间拷贝到用户空间:第二拷贝:copy_to_user()

这种传统的 IPC 通信方式有两个问题:

  1. 性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
  2. 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,

因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

问题:为啥经过内核,不直接拷贝呢?

由于进程隔离机制的存在,通信双方必然需要借助 Binder 来实现。

进程隔离

操作系统为了保证自身的安全稳定性,将系统内核空间和用户空间分离开来,保证用户程序进程崩溃时不会影响到整个系统,简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(UserSpace)是用户程序运行的空间。为了保证安全性,它们之间是隔离的,所以用户空间的进程要进行交互需要通过内核空间来驱动整个过程

一次拷贝的图解:

[图片上传失败...(image-a35672-1642817053966)]

Binder IPC****正是基于内存映射(****mmap****)来实现的

内存映射:Binder IPC 机制中涉及到的内存映射通过mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映

射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。


一次拷贝过程通常是这样:

  1. 首先Binder 驱动在内核空间创建一个数据接收缓存区;

  2. 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收

缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;(数据缓存区-------->数据接收缓存区)

  1. 发送方进程通过系统调用copyfromuser() 将数据copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也

就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信


问题: 内存映射是如何解决浪费空间的问题?

驱动为接收方分担了最为繁琐的任务:分配/释放大小不等,难以预测的有效负荷缓存区,而接收方只需要提供缓存来存放大小固定,要么动态扩容,最大空间可以预测的消息头即可。在效率上,由于mmap()分配的内存是映射在接收方用户空间里的,所有总体效果就相当于对有效负荷数据做了一次从发送方用户空间到接收方用户空间的直接数据拷贝,省去了内核中暂存这个步骤,提升了一倍的性能(通过data_buffer等方式让数据仅包含定长的消息头,解决了接受端内存分配的问题)

[图片上传失败...(image-130108-1642817053968)]

ServiceManager、Binder Client、Binder Server 处于不同的进程,他们三个都在用户空间,而Binder 驱动在内核空间。

Server 进程向ServiceManager 注册一个映射关系表

自己的理解:

重点4者的关系作用需要体现,触发过程,

1).servermanager:注册过程:循环等待,4个红黑树

2).client调用: 代理

3).binder驱动:内存映射,进程之间相互隔离

4).服务端返回:代理

从源码的角度分析了一次通信的流程(不错)【偏注册,内存映射】

第一步:首先需要注册服务端,只有注册了服务端,客户端才有通讯的目标,服务端通过 ServiceManager 注册服务,注册的过程就是向 Binder 驱动的全局链表 binder_procs 中插入服务端的信息(binder_proc 结构体,每个 binder_proc 结构体中都有 todo 任务队列),然后向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务。

第二步:有了服务端,客户端就可以跟服务端通讯了,通讯之前需要先获取到服务,拿到服务的代理,也可以理解为引用。比如下面的代码:

//获取WindowManager服务引用

WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);

获取服务端的方式就是通过 ServiceManager 向 svcinfo 列表中查询一下返回服务端的代理,svcinfo 列表就是所有已注册服务的通讯录,保存了所有注册的服务信息。

第三步:有了服务端的引用我们就可以向服务端发送请求了,通过 BinderProxy 将我们的请求参数发送给 ServiceManager,

第四步:通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态,

第五步: 然后 Binder 驱动向服务端的 todo 队列里面插入一条事务,执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间,

第六步: 唤醒等待的客户端并把结果响应回来,这样就完成了一次通讯。

————————————————


问题:一次通讯过程中,讲清楚client,server,servermanager,binder驱动关系!(从AIDL角度)

简单描述流程

进程相互隔离,为了安全,不能直接通讯,所以binder驱动来了

1.server会把自己注册到serverManager(通过binder驱动):表示可以提供服务了,创建了实体和servermanger对实体的引用

2.当Client 进程向ServiceManager 查询Server 进程(通过binder驱动)Binder 实体的引用,后面通过这个引用就能实现和Server 进程的通信。

3.将Server进程中的真实对象转换成代理对象,返回这个代理对象给Client 进程

4.Client进程拿到了这个代理对象,然后调用这个代理对象的方法(通过binder驱动) Binder驱动继续发挥他的使命,它会通知Server进程执行计算工作

5.将Server进程中的真实对象执行的结果返回给了Client进程,这样Client进程还是如愿的得到了自己想要。跨进程通信完毕

客户端调用服务端具体方法的流程:

Client想调用Service中Object对象的add()方法,需要通过Binder驱动向SM查找表中查询是否有Object对象的add()方法,有则返回Client一个Object对象的代理对象Proxy ;

Client 调用代理对象Proxy的add()方法,Binder驱动进行接收处理去调用真实对象Object的add()方法;

Object的add()方法执行后返回结果给SM,SM通过Binder驱动将结果返回给Client。


Binderj机制的通信过程和联系(关注下代理关系)

1.客户端获取服务端的代理对象(proxy)。我们需要明确的是客户端进程并不能直接操作服务端中的方法,如果要操作服务端中的方法,那么有一个可行的解决方法就是在客户端建立一个服务端进程的代理对象,这个代理对象具备和服务端进程一样的功能,要访问服务端进程中的某个方法,只需要访问代理对象中对应的方法即可;

2.客户端通过调用代理对象向服务端发送请求。将参数打包成parcel对象,Binder的代理对象会调用transact(),同时会阻塞当前线程,等待transact()结束,返回结果。

3.代理对象将用户请求通过Binder驱动发送到服务器进程;

4.服务端进程处理客户端发过来的请求,处理完之后通过Binder驱动返回处理结果给客户端的服务端代理对象;

5.代理对象将请求结果进一步返回给客户端进程。(服务端会在onTransact()收到调用方的调用,通过code知道调用的是add服务,通过传过来的parcel解析出参数)

通过以上5个步骤,就完成了一次Binder通信。

为什么要用代理呢?

客户端获取服务端的代理对象(proxy)。我们需要明确的是客户端进程并不能直接操作服务端中的方法,

如果要操作服务端中的方法,那么有一个可行的解决方法就是在客户端建立一个服务端进程的代理对象,

这个代理对象具备和服务端进程一样的功能,要访问服务端进程中的某个方法,只需要访问代理对象中对应的方法即可;

关于代理:

原本从SM中拿到binder的引用,通过Binder驱动层的处理之后,返回给了client一个代理对象,实际上如果client和server处于同一个进程,返回的就是当前binder对象,如果client和server不处于同一个进程,返回给client的就是一个代理对象

代理模式(Proxy Pattern ) 在Android中client不是直接去和binder打交道,client直接和Manager交互,而manager和managerProxy交互,也就是说client是通过managerProxy去和binder进行交互的。同时service也不是直接和binder交互,而是通过stub去和binder交互。

[图片上传失败...(image-292a69-1642817053948)]


举个列子,AIDL:

实际案例:

1.跨进程的时候aidl用service?不用行不行?

不行,需要拿到服务端的代码对象,这样才能调用服务端另外一个进程的方法

流程:在另外一个进程启动service。然后得到binder。然后得到实例。然后调用方法

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">private val mConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val imageManager: IImageManager = IImageManager.Stub.asInterface(service)
imageManager.doTask(44)
}

override fun onServiceDisconnected(name: ComponentName) {
}

}
</pre>

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class AblumManagerService extends Service {
private static final String TAG = "ImageManagerService"; private Binder mBinder = new IImageManager.Stub() {
@Override
public void doTask(int count) throws RemoteException {
Log.d("ImageManagerService", "doTask");
Thread thread = new Thread() {
@Override
public void run() {
Log.e(TAG, "startImageScanner:size-->");
}
};
thread.start();
}
}; @Nullable
@Override public IBinder onBind(Intent intent) {
Log.d("ImageManagerService", "onBind");
return mBinder;
}
}
</pre>

不用服务的话:实现链接。然后传入binder得到实例,和上面的区别就是没有用service。service是为了在另外一个进程吗?都叫远程服务?

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace;">val mBinder: Binder = object : IImageManager.Stub() {
@Throws(RemoteException::class)
override fun doTask(count: Int) {
}
}

val imageManager: IImageManager = IImageManager.Stub.asInterface(mBinder)
imageManager.doTask(444)</pre>

</pre>

通过asInterface接口,把binder转为代理对象

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;"> public static com.baoneng.cloudserviceapp.image.aidl.IImageManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.baoneng.cloudserviceapp.image.aidl.IImageManager))) {
return ((com.baoneng.cloudserviceapp.image.aidl.IImageManager) iin);
}
return new com.baoneng.cloudserviceapp.image.aidl.IImageManager.Stub.Proxy(obj);
}</pre>

我们先从主进程启动另外一个进程

然后得到binder。

然后通过binder调用另外一个进程的方法

怎么实现一个进程调用另外一个进程的方法!

  • 步骤1: 过程描述:Server进程 通过Binder驱动 向 Service Manager进程 注册服

Server进程 创建 一个 Binder 对象(代码就是service里面的,启动服务里面返回binder)

步骤2:获取服务

  • Client进程 使用 某个 service前(此处是 相加函数),须 通过Binder驱动 向 ServiceManager进程 获取相应的Service信息
  • 源码:获取binder的代理对象

步骤3:使用服务

Client进程 根据获取到的 Service信息(Binder代理对象),通过Binder驱动 建立与 该Service所在Server进程通信的链路,并开始使用服务

序列化 :transact

  android.os.Parcel data = android.os.Parcel.obtain();
  data.writeInt(a); 
  data.writeInt(b); 

  data.writeInterfaceToken("add two int");;
  // 方法对象标识符让Server进程在Binder对象中根据"add two int"通过queryLocalIInterface()查找相应的IInterface对象(即Server创建的plus),Client进程需要调用的相加方法就在该对象中

  android.os.Parcel reply = android.os.Parcel.obtain();
  // reply:目标方法执行后的结果(此处是相加后的结果)

// 2\. 客户端通过 调用代理对象的transact() 将 上述数据发送到Binder驱动
  binderproxy.transact(Stub.add, data, reply, 0)来源: //www.greatytc.com/p/4ee3fd07da14

调用代理的方法:

  // 1\. Binder驱动根据 代理对象 沿原路 将结果返回 并通知Client进程获取返回结果
  // 2\. 通过代理对象 接收结果(之前被挂起的线程被唤醒)

    binderproxy.transact(Stub.ADD, data, reply, 0);
    reply.readException();;
    result = reply.readInt();
          }
}

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">boolean _status = mRemote.transact(Stub.TRANSACTION_doTask, _data, _reply, 0);</pre>

Ibinder:是连接成功,返回回来的。返回远端

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">override fun onServiceConnected(name: ComponentName, service: IBinder) {</pre>

通过asinterface把ibinder。转成远端代理.

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public static com.baoneng.cloudserviceapp.image.aidl.IImageManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.baoneng.cloudserviceapp.image.aidl.IImageManager))) {
return ((com.baoneng.cloudserviceapp.image.aidl.IImageManager) iin);
}
return new com.baoneng.cloudserviceapp.image.aidl.IImageManager.Stub.Proxy(obj); }</pre>

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">private static class Proxy implements com.baoneng.cloudserviceapp.image.aidl.IImageManager {
private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override

public void doTask(int count) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(count);
boolean _status = mRemote.transact(Stub.TRANSACTION_doTask, _data, _reply, 0); }
} }</pre>

关于 transact() 方法:这个是ibinder接口提供的。这是客户端和服务端通信的核心方法。调用这个方法之后,客户端将会挂起当前线程,等候服务端执行完相关任务后通知并接收返回的 _reply 数据流。关于这个方法的传参,这里有两点需要说明的地方:

  1. 如何知道客户端需要调用哪个进程以及该进程中的函数

private static final java.lang.String DESCRIPTOR = "com.baoneng.cloudserviceapp.image.aidl.IImageManager";

要给每个需要远程通信的类唯一标识就可以通过包名+类名的字符串就可以做到,然后在类里面给每个函数编号即可对函数唯一编码

  • 方法 ID :transact() 方法的第一个参数是一个方法 ID ,这个是客户端与服务端约定好的给方法的编码,彼此一一对应。在AIDL文件转化为 .java 文件的时候,系统将会自动给AIDL文件里面的每一个方法自动分配一个方法 ID。
  • 第四个参数:transact() 方法的第四个参数是一个 int 值,它的作用是设置进行 IPC 的模式,为 0 表示数据可以双向流通,即 _reply 流可以正常的携带数据回来,如果为 1 的话那么数据将只能单向流通,从服务端回来的 _reply 流将不携带任何数据。
    注:AIDL生成的 .java 文件的这个参数均为 0。

源码分析:

aidl是接口,自动生成的java文件如下:

声明作为 Server 端的远程 Service 具有哪些能力

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public interface IImageManager extends android.os.IInterface {
/**

  • Default implementation for IImageManager. */ public static class Default implements com.baoneng.cloudserviceapp.image.aidl.IImageManager {
    @Override
    public void doTask(int count) throws android.os.RemoteException {
    }

     @Override
    

public android.os.IBinder asBinder() {
return null;
}
}

/**
  • Local-side IPC implementation stub class. / public static abstract class Stub extends android.os.Binder implements com.baoneng.cloudserviceapp.image.aidl.IImageManager {
    private static final java.lang.String DESCRIPTOR = "com.baoneng.cloudserviceapp.image.aidl.IImageManager"; /
    *

  • Construct the stub at attach it to the interface. */ public Stub() {
    this.attachInterface(this, DESCRIPTOR);
    }

     /**
    
  • Cast an IBinder object into an com.baoneng.cloudserviceapp.image.aidl.IImageManager interface, * generating a proxy if needed. */ public static com.baoneng.cloudserviceapp.image.aidl.IImageManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.baoneng.cloudserviceapp.image.aidl.IImageManager))) {
    return ((com.baoneng.cloudserviceapp.image.aidl.IImageManager) iin);
    }
    return new com.baoneng.cloudserviceapp.image.aidl.IImageManager.Stub.Proxy(obj);
    }

     @Override
    

public android.os.IBinder asBinder() {
return this;
}

    @Override

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true; }
case TRANSACTION_doTask: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.doTask(_arg0);
reply.writeNoException();
return true; }
default: {
return super.onTransact(code, data, reply, flags);
}
}
}

    private static class Proxy implements com.baoneng.cloudserviceapp.image.aidl.IImageManager {
        private android.os.IBinder mRemote;    Proxy(android.os.IBinder remote) {
            mRemote = remote;

}

        @Override

public android.os.IBinder asBinder() {
return mRemote;
}

        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;

}

        @Override

public void doTask(int count) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(count);
boolean _status = mRemote.transact(Stub.TRANSACTION_doTask, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().doTask(count);
return; }
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}

        public static com.baoneng.cloudserviceapp.image.aidl.IImageManager sDefaultImpl;

}

    static final int TRANSACTION_doTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);   public static boolean setDefaultImpl(com.baoneng.cloudserviceapp.image.aidl.IImageManager impl) {
        // Only one user of this interface can use this function

// at a time. This is a heuristic to detect if two different // users in the same process use this function. if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true; }
return false;
}

    public static com.baoneng.cloudserviceapp.image.aidl.IImageManager getDefaultImpl() {
        return Stub.Proxy.sDefaultImpl;

}
}

public void doTask(int count) throws android.os.RemoteException; }

</pre>

里面2个静态内部类stub和proxy,都实现了Ibinder接口,然后自己实现了iinterface()接口

重点:要分清哪个是服务端,哪个是客户端?(还是没搞懂)

1.使用的时候,实现stub接口,得到binder对象。所以它是服务端

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">private Binder mBinder = new IImageManager.Stub() {
@Override
public void doTask(int count) throws RemoteException {
Log.d("ImageManagerService", "doTask");
Thread thread = new Thread() {
@Override
public void run() {
Log.e(TAG, "startImageScanner:size-->");
}
};
thread.start();
}
};</pre>

实现一个跨进程调用对象Stub。Stub 继承Binder, 说明它是一个Binder 本地对象;实现IInterface 接口,表明具有Server 承诺给Client 的能力

具体怎么得到对象: 方法中会去调用binder.queryLocalInterface() 去查找Binder 本地对象

通过descriptor进行查找

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">private static final java.lang.String DESCRIPTOR = "com.baoneng.cloudserviceapp.image.aidl.IImageManager";</pre>

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);</pre>

  1. Client 进程中,拿到了代理对象,然后 执行addBook() 的线程挂起等待返回

此,如果远程进程是执行长时间的运算,请不要使用主线程去调用远程函数,以防止ANR。

  1. proxy调用Server 进程本地对象IBinder的Transact()

最终又走到了Stub 中的onTransact()

在Server进程里面,onTransact根据调用号(每个AIDL函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数

几个类的详细解释:

  1. **IInterface **

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public interface IImageManager extends android.os.IInterface</pre>

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public interface IInterface
{ public IBinder asBinder(); }
</pre>

2.IBinder

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-size: 0.817rem;">public class Binder implements IBinder {</pre>

在这里,如果链接成功返回回来的。

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">private val mConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val imageManager: IImageManager = IImageManager.Stub.asInterface(service)</pre>

3.Binder

4.IImageManager 自动生成的接口

5.stub:而这个类最核心的成员是 Stub类 和 Stub的内部代理类Proxy。

Stub充当服务端角色,持有Binder实体(本地对象)。远程进程执行onTransact()函数

  • 获取客户端传过来的数据,根据方法 ID 执行相应操作。
  • 将传过来的数据取出来,调用本地写好的对应方法。
  • 将需要回传的数据写入 reply 流,传回客户端。

2) Stub(服务端Stub 是读取参数,写入值)

Stub(把客户端的数据读取,然后通过onTransact写入值)

Stub运行在服务器端,继承自Binder,同样也实现了IMyService 接口,它的核心逻辑在onTransact方法中:

Stub中另外一个比较重要的接口就是asInterface()接口

通过方法名字,我们大致可以猜出,它大概实现的功能,就是将一个IBinder对象转化为接口对象

private android.os.IBinder mRemote;

每次调用接口的方法:getValue()【自己定义的】

都会执行_data.writeInterfaceToken(DESCRIPTOR);

<pre style="margin: 8px 0px; color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43);">

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace;">private Binder mBinder = new IImageManager.Stub() {
@Override
public void doTask(int count) throws RemoteException {
Log.d("ImageManagerService", "doTask");
Thread thread = new Thread() {
@Override
public void run() {
Log.e(TAG, "startImageScanner:size-->");
}
};
thread.start();
}
}; </pre>

</pre>

<pre style="margin: 8px 0px;">6.Proxy代理类充当客户端角色,服务端的本地代理 ,持有Binder引用(句柄)。(客户端:写入参数,读取值)</pre>

<pre style="margin: 8px 0px; color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43);"> * 生成 _data 和 _reply 数据流,并向 _data 中存入客户端的数据。

  • 通过 transact() 方法将它们传递给服务端,并请求服务端调用指定方法。
  • 接收 _reply 数据流,并从中取出服务端传回来的数据。 </pre>

Proxy(把客户端的参数序列化之后transact到服务端,然后读到的就是服务端的方法的结果)

getValue(自己定义的接口方法)这个方法里面的下面几个步骤。首先是将left 和 right的参数写入到_data中去,同时在 远程binder 调用结束后,得到返回的 _reply ,在没有异常的情况下,返回_reply.readLong()的结果。

Proxy运行在客户端,它实现了IMyService (自己定义的)接口,并且持有一个远程代理IBinder mRemote,mRemote不做任何业务逻辑处理,仅仅通过IBinder接口的transact()方法,把客户端的调用参数序列化后transact到远程服务器。

四.AIDL如何实现监听

原理:server提供监听接口就可以。客户端注册就可以了。不用remoteListener()

监听接口定义:”

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: Menlo, monospace; font-size: 0.8rem;">
interface IServiceAutoStepInterface {
/**

  • Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void registerListener(IStepCountListener listener);
    void unRegisterListener(IStepCountListener listener);
    int getCurStepCount();
    void onStepDetectedCallBack(int step, long st, long et);
    }
    </pre>

服务端:

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: Menlo, monospace; font-size: 0.8rem;">public class ServiceAutoStep extends Service {
public static final String WIDGET_TYPE_VIDEO = "video";
public static final String WIDGET_TYPE_TPOIC = "topic"; @Nullable
@Override public IBinder onBind(Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setForegroundService();
}
return mBinder;
}</pre>

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: Menlo, monospace; font-size: 0.8rem;"> private IBinder mBinder = new IServiceAutoStepInterface.Stub() {
@Override
public void registerListener(IStepCountListener listener) throws android.os.RemoteException {
mArrayList.clear();
if (null != listener) {
if (-1 == getStepCountListenerIndex(listener)) {
mArrayList.add(listener);
YDLog.logWannig(kTag, "register listener ,list not found");
}
YDLog.logInfo(kTag, "register listener ,cur size :", mArrayList.size());
} else {
// throw new android.os.RemoteException();
YDLog.logError(kTag,"registerListener android.os.RemoteException ");
}
}

    @Override

public void unRegisterListener(IStepCountListener listener) throws android.os.RemoteException {
int index = getStepCountListenerIndex(listener);
mArrayList.clear(); // if (index >= 0 && index < mArrayList.size()) { // mArrayList.remove(index);
YDLog.logInfo(kTag, "unRegister listener ,remove succ"); // }
YDLog.logInfo(kTag, "unRegister listener,callback");
}

    @Override

public int getCurStepCount() throws android.os.RemoteException {
return todayStepCount;
}

    @Override

public void onStepDetectedCallBack(int step, long st, long et) throws RemoteException {

    }
};</pre>

客户端调用:

启动服务

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-size: 0.8rem;">private boolean bindStepService() {
Activity activity = getReference();
if (activity != null) {
try {
Intent service = new Intent(activity, ModuleHub.moduleSport().getStepService());
return activity.bindService(service, mStepConnect, Context.BIND_IMPORTANT);
} catch (Exception e) {
return false;
}
}
return false; }</pre>

注册远程监听

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-size: 0.8rem;">private StepCountListener mStepCountListener = new StepCountListener(this); private IServiceAutoStepInterface mServiceAutoStepInterface; private ServiceConnection mStepConnect = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceAutoStepInterface = IServiceAutoStepInterface.Stub.asInterface(service);
try {
mServiceAutoStepInterface.registerListener(mStepCountListener);
} catch (Throwable e) {
}
}

@Override

public void onServiceDisconnected(ComponentName name) {
try {
//mServiceAutoStepInterface android.os.DeadObjectException
if (!AndroidUtils.haveRunProcess(ShadowApp.context()) && mServiceAutoStepInterface != null)
mServiceAutoStepInterface.unRegisterListener(mStepCountListener);
mStepCountListener = null;
} catch (Throwable e) {
e.printStackTrace();
}
mServiceAutoStepInterface = null;
}
};</pre>

五.Binder机制缺点:

1)。就是不同利用Binder输出大数据,比如利用Binder传输几M大小的图片,便会出现异常

2).binder通信要用binder线程池,超过会阻塞。

传统的linux的IPC跨进程比较:

  1. 管道(Pipe):在创建时分配一个page大小的内存,缓存区大小比较有限;

详细: 相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这 个进程会向管道中放入信息

[图片上传失败...(image-e4e8bc-1642817053952)]

  1. 共享内存(memeory):无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;

会容易出现死锁的问题

安全问题:比如我要给进程2用,结果发现3用了,非常不安全

3.套接字(socket):作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;

  1. 信号(signal): 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

所以binder优点:1).安全比共享内存 ,可以获取uid和pid 2)。性能好比管道 3)。支持CS架构,架构清晰,职责明确

Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高。

项目 评价 描述
正确性 支持多进程安全, 使用 mmap, 由操作系统保证数据回写的正确性
时间开销 使用 mmap 实现, 减少了用户空间数据到内核空间的拷贝
空间开销 使用 protocl buffer 存储数据, 同样的数据会比 xml 和 json 消耗空间小
使用的是数据追加到末尾的方式, 只有到达一定阈值之后才会触发键值合并, 不合并之前会导致同一个 key 存在多份
安全 使用 crc 校验, 甄别文件系统和操作系统不稳定导致的异常数据
开发成本 使用方式较为简单
兼容性 各个安卓版本都前后兼容

总结的还不错!

一篇文章了解相见恨晚的 Android Binder 进程间通讯机制_jeanboy-CSDN博客_android binder机制(非常好,驱动层)

https://juejin.cn/post/6844903589635162126 (java层,牛逼)

https://blog.csdn.net/carson_ho/article/details/73560642(还可以,JAVA层)

最好的一些讲解!(系列文章)

https://cloud.tencent.com/developer/user/4918971/search/article-binder

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容