2.Android Binder图解 从 Java层解析Binder通信全流程拆解

1.内存映射与一次拷贝机制

1.1. 用户空间和内核空间

空间 权限 访问限制 Binder作用
用户空间 低特权 禁止直接访问硬件/其他进程 存放应用数据
内核空间 高特权 可管理所有进程和硬件 中转数据,执行内存映射

1.2 进程隔离

操作系统为了保证自身的安全稳定性,将系统内核空间和用户空间分离开来,保证用户程序进程崩溃时不会影响到整个系统,简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(UserSpace)是用户程序运行的空间。

为了保证安全性,它们之间是隔离的,所以用户空间的进程要进行交互需要通过内核空间来驱动整个过程

1.3 内存映射

  • 进程启动时,在内核空间映射 1MB-8KB 的共享内存区域。
  • 映射关系:用户空间 ←→ 内核空间 ←→ 物理页面(无需数据复制)。
  • 代码体现binder_mmap() 创建内核缓冲区,建立双端映射

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

rust 复制代码
// 内核关键调用
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) {
    // 1. 分配物理页
    struct page *page = alloc_page(GFP_KERNEL); 
    // 2. 映射用户空间:用户虚拟地址 → 物理页
    remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), PAGE_SIZE, vma->vm_page_prot);
    // 3. 映射内核空间:内核虚拟地址 → 同一物理页
    vma->vm_private_data = binder_buffer;
}
  • 效果
    • 用户空间修改 → 物理页更新 → 内核空间立即可见(无需复制)
    • 内核空间修改 → 物理页更新 → 用户空间立即可见

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

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

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

1.4 非一次拷贝和一次拷贝

1.4.1 非一次拷贝

copy_from_user() //将数据从用户空间拷贝到内核空间

copy_to_user() //将数据从内核空间拷贝到用户空间

  • 2次拷贝:用户空间→内核→用户空间,性能差

图片:

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

图片:

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

  • 2).接着在内核空间开辟一块内核缓存区,建立内核缓存区内核中数据接收缓存区 之间的映射关系,以及内核中数据接收缓存区接收进程用户空间地址的映射关系;

  • 3).发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信

优势
  • 效率:避免重复拷贝,性能提升1倍。

  • 安全:内核校验PID/UID,支持实名/匿名Binder。

  • 稳定性:内核管理内存分配,避免接收方缓冲区溢出。

2.Binder机制的原理:从JAVA层深度理解

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

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

2.1 简单的理解:

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

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

2).client调用: 代理

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

4).服务端返回:代理

2.2 Binder 通信过程:

  1. 首先,一个进程使用 BINDER_SET_CONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;

  2. Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。

  3. Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

2.3 详细点: 从源码的角度分析了一次通信的流程【偏注册,内存映射】

第一步:首先需要注册服务端,只有注册了服务端,客户端才有通讯的目标,服务端通过 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() 将内核的结果拷贝到用户空间,

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

3.AIDL的原理

3.1 从AIDL的源码中,一次通讯过程中,讲清楚client,server,servermanager,binder驱动4者的关系!(从AIDL角度)

关键角色职责
角色 作用
ServiceManager 服务的"电话簿",管理所有实名Binder的注册与查询(handle=0的特殊Binder)
Binder驱动 内核模块,负责: • 进程间数据路由 • 线程调度与阻塞唤醒 • 内存映射管理
Server 真实服务提供者,业务逻辑实现者(如MediaPlayerService)
Client 服务消费者,通过Proxy调用远程服务

3.1.1 简单描述流程

进程相互隔离,为了安全,不能直接通讯,所以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进程还是如愿的得到了自己想要。跨进程通信完毕

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

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

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

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

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

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

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

3.1.3 核心流程(以AIDL为例)

  1. 服务注册

    • Server进程 通过Binder驱动向ServiceManager注册服务(如addService)。
    • 驱动在内核空间创建binder_proc结构体,插入全局链表binder_procs,并缓存服务信息到ServiceManager的svcinfo列表。
  2. 服务发现

    • Client进程 通过ServiceManager.getService()查询服务,获取服务的代理对象BpBinder)。
    • 若Client与Server同进程,返回本地对象;否则返回代理对象(跨进程通信桥梁)。
  3. 跨进程调用

    • Client调用代理对象方法(如imageManager.doTask()):

      • Proxy 序列化参数(Parcel),通过transact()发送请求到Binder驱动。
      • 客户端线程挂起等待返回(非oneway模式)。
    • Binder驱动

      • 通过copy_from_user()将数据一次拷贝到内核空间(内存映射实现)。
      • 向Server进程的todo队列插入事务,唤醒服务端线程。
    • Server进程

      • onTransact()中根据方法ID解析参数,执行实际逻辑。
      • 结果写入reply Parcel,通过copy_to_user()返回内核(实际无拷贝)。
    • Client

      • 被唤醒,从reply中读取结果,完成调用。
3.1.3.2 客户端调用服务端具体方法的流程:

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

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

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


3.2 AIDL的demo

3.3 AIDL会遇到哪些坑

之前项目写的.aidl文件编译时一直无法生成java文件,排查很久发现原因是aidl文件中的注释包含了中文导致的

问题 原因 解决方案
参数顺序变更导致调用失败 方法按编号匹配,新增方法需放最后 永远在AIDL接口末尾添加新方法
传输大图抛TransactionTooLargeException Binder限制1MB-8KB 改用ContentProvider或共享内存传输
回调接口泄露 未及时反注册Listener 使用RemoteCallbackList自动管理
死锁 服务端同步调用客户端回调 避免关键路径同步回调,改用oneway

3.3.1 问题: AIDL in out oneWay代表什么意思?

在 Android 中,AIDL(Android Interface Definition Language)是一种用于定义客户端与服务端之间通信接口的语言。在 AIDL 中,有三种关键字用于声明参数的传递方式:in、out 和 inout。此外,还有一个修饰符 oneWay。

in:表示客户端向服务端传递数据,服务端接收并使用该数据,但不会将结果返回给客户端。这意味着数据是从客户端到服务端的单向传递。

out:表示服务端向客户端传递数据,服务端会向该参数写入数据,而客户端会读取该数据。通常情况下,这些参数会被服务端修改,然后返回给客户端。

inout:表示数据可以在客户端和服务端之间双向传递。客户端会传递数据给服务端,服务端会修改数据并返回给客户端。 oneWay 关键字表示该方法是一个单向通信的方法,即客户端调用后不需要等待服务端的返回结果即可继续执行下一步操作。这在需要执行耗时操作而不关心返回结果的情况下很有用。使用 oneWay 关键字声明的方法只能有 in 类型的参数,而不能有 out 或 inout 类型的参数。

3.3.2 AIDL实现双向通信

方法一: 2边都启动Service

方法二:通过监听

AIDL如何实现监听 ?

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

监听接口定义:"

csharp 复制代码
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);
}

服务端:

java 复制代码
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;
    }

    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 {

        }
    };

客户端调用:

启动服务

java 复制代码
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;
}

注册远程监听

java 复制代码
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;
    }
};

asInterface的里面:判断是否通一个进程,否则用代理对象

ServiceConnection里面的回调方法运行在哪个线程?

ServiceConnection里面的回调方法也是运行在UI线程

Service的onCreate、onStartCommand、onDestory等全部生命周期方法都运行在UI线程,

3.4 AIDL源码分析Binder机制的整个过程

实际案例:

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

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

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

kotlin 复制代码
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) {
    }
}
java 复制代码
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;
    }
}

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

kotlin 复制代码
val mBinder: Binder = object : IImageManager.Stub() {
    @Throws(RemoteException::class)
    override fun doTask(count: Int) {
    }
}

val imageManager: IImageManager = IImageManager.Stub.asInterface(mBinder)
imageManager.doTask(444)

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

typescript 复制代码
     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);
        }

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

然后得到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

kotlin 复制代码
  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)

来源: https://www.jianshu.com/p/4ee3fd07da14

调用代理的方法:

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

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

boolean _status = mRemote.transact(Stub.TRANSACTION_doTask, _data, _reply, 0);

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

kotlin 复制代码
override fun onServiceConnected(name: ComponentName, service: IBinder) {

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

java 复制代码
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);
}



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);

        }
    }
}

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

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

arduino 复制代码
  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 具有哪些能力?

java 复制代码
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;
}

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

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

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

java 复制代码
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();
    }
};

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

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

通过descriptor进行查找

less 复制代码
private static final java.lang.String DESCRIPTOR = "com.baoneng.cloudserviceapp.image.aidl.IImageManager";

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);

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

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

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

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

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

客户端调用的时候会挂起,底层源码分析

flag的同步和异步

几个类的详细解释:

  1. IInterface , IInterface 代表的就是 Server 进程对象具备什么样的能力
java 复制代码
public interface IImageManager extends android.os.IInterface

public interface IInterface
{
    public IBinder asBinder();
}
  1. .IBinder, IBinder 是一个接口,代表了一种跨进程通信的能力。只要实现了这个借口,这个对象就能跨进程传输。
java 复制代码
public class Binder implements IBinder {

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

kotlin 复制代码
private val mConnection: ServiceConnection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName, service: IBinder) {
        val imageManager: IImageManager = IImageManager.Stub.asInterface(service)

3.5 AIDL的代理模式

3.5.1 关于代理:

原本从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交互。

3.4.2 AIDL的核心接口和对象

3.4.2.1 代理模式核心
  • Proxy(客户端)

    • 持有IBinder远程引用(mRemote)。
    • 将参数序列化,通过transact()发送请求,阻塞等待结果。
  • Stub(服务端)

    • 继承Binder,实现实际业务逻辑。
    • onTransact()中解析请求,执行方法并返回结果。
  • 关键方法

    • transact():客户端 → 驱动(同步阻塞)。
    • onTransact():驱动 → 服务端(执行实际逻辑)
3.4.2.2 对象职责
  • IInterface :声明服务能力(如doTask())。

  • IBinder :跨进程通信能力基接口(transact()为通信核心)。

  • Binder :服务端本地对象基类,处理驱动回调(onTransact())。

  • Stub

    • 服务端:继承Binder,实现业务逻辑。
    • 提供asInterface():将IBinder转换为本地对象或代理。
  • Proxy :客户端代理,封装参数序列化和transact()调用。

3).Binder Java 层的 Binder 类,代表的其实就是 Binder 本地对象

4).stub: 自动生成的, 而这个类最核心的成员是 Stub类 和 Stub的内部代理类Proxy。

这个类继承了 Binder,它实现了 IInterface 接口!

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

  • 获取客户端传过来的数据,根据方法 ID 执行相应操作。
  • 将传过来的数据取出来,调用本地写好的对应方法。
  • 将需要回传的数据写入 reply 流,传回客户端。
这个类继承了 Binder5) .IImageManager 自动生成的接口
2) Stub(服务端Stub 是读取参数,写入值)

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

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

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

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

private android.os.IBinder mRemote;

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

都会执行_data.writeInterfaceToken(DESCRIPTOR);

java 复制代码
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();
    }
};

整理过程:Proxy代理类充当客户端角色,服务端的本地代理 ,持有Binder引用(句柄)。(客户端:写入参数,读取值)

生成 _data 和 _reply 数据流,并向 _data 中存入客户端的数据。通过 transact() 方法将它们传递给服务端,并请求服务端调用指定方法。接收 _reply 数据流,并从中取出服务端传回来的数据。

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

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

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

3.5.2 案例

流程总结:

1.在 Proxy 中的 addBook() 方法中首先通过 Parcel 将数据序列化,然后调用 remote.transact()

  1. Client 进程通过系统调用陷入内核态,Client 进程中执行 addBook(), 里面 transact()的线程挂起等待返回;

  2. 驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact()。最终又走到了 Stub 中的 onTransact() 中

  3. onTransact() 根据函数编号调用相关函数(在 Stub 类中为 BookManager 接口中的每个函数中定义了一个编号,只不过上面的源码中我们简化掉了;在跨进程调用的时候,不会传递函数而是传递编号来指明要调用哪个函数

onTransact()代表回调, transact()代表主动!

3.5.3 AIDL和代理模式的总结:(根据AIDL生成的源码总结)

stub是总类(服务端),Proxy是里面的一个子类(客户端)

1). 客户端启动服务,通过Stub.asInterface(), 里面调用查找, 通过描述符, 找到服务端代理或本地

2). 服务端起来的时候通过实现stub返回ibinder对象

3)客户端调用 ,Proxy 中, 的 addBook() 方法中首先通过 Parcel 将数据序列化,然后调用 ,通过服务端代理对象, remote.transact()

4). Client 进程通过系统调用陷入内核态,Client 进程中执行 addBook(), 里面 transact()的线程挂起等待返回;

5). 驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact()。最终又走到了 Stub 中的 onTransact() 中

4.Binder和跨进程方案对比

方案 性能 安全性 适用场景 缺点
Binder 高频调用(如系统服务、App IPC) 数据量受限(~1MB)
共享内存 极高 大数据传输(图像/视频) 需同步机制,易死锁
Socket 网络通信或跨设备 开销大,需要协议封装
ContentProvider 结构化数据共享(数据库/文件) 基于Binder封装,效率中等
文件 极低 简单数据持久化 并发差,实时性低
Binder的绝对优势

性能:一次拷贝+内存映射,远超Socket/文件

Binder的局限性
  • 数据量限制 :传输超过 1MB-8KB 数据会抛TransactionTooLargeException
    解决方案 :改用ContentProviderSocket传输大文件。
  • 线程池阻塞 :默认16个线程,满时新请求阻塞。
    监控方案 :Hook Binder调用链,检测卡顿(参考微信Matrix)。
相关推荐
AlienZHOU3 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc6 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师7 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen8 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒8 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow8 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8188 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800788 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明8 小时前
第 6 章 组件进阶
前端·vue.js
江华森8 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端