图解 Binder:AIDL

这是一系列的 Binder 文章,会从内核层到 Framework 层,再到应用层,深入浅出,介绍整个 Binder 的设计。详见《图解 Binder:概述》。

本文基于 Android platform 分支 android-13.0.0_r1 和内核分支 common-android13-5.15 解析。

一些关键代码的链接,可能会因为源码的变动,发生位置偏移、丢失等现象。可以搜索函数名,重新进行定位。

AIDL(Android Interface Definition Language)是一种支持跨进程通信 (IPC) 的接口定义语言。在 Android 系统中,各个进程通常使用 Binder 通信。Android 提供的 AIDL 也是基于 Binder 的。通过 AIDL,我们可以利用其他进程的 Service,调用其他进程的函数。

AIDL 的作用,其实就是为开发者生成两个类:Stub 和 Proxy。这两个类都是一些模板代码,主要目的除了减轻开发者的工作量以外,也是为了隐藏跨进程通信 (IPC) 的各种细节在这两个类中,使得开发者只需要关注实现具体的服务逻辑,而无需关心底层通信的细节。

Stub 通常用于服务端,Proxy 通常用于客户端。

AIDL 的使用

要在 Java 中使用 AIDL,我们需要执行以下步骤:

  1. 无论是客户端,还是服务端,都要定义 AIDL 接口:创建一个后缀为 .aidl 的文件,并在其中定义一个接口。接口中可以声明需要跨进程调用的函数。例如,创建一个名为 IExampleService.aidl 的文件,其内容如下:

    package com.example;
    
    
    
    interface IExampleService {
          String getMessage();
    }
    
    
  2. 编译生成代码:编译项目时,AIDL 编译器会自动为定义的 AIDL 接口生成对应的 Java 文件:IExampleService.java。生成的文件里包括了 IExampleService 接口、Stub 类和 Proxy 类。

  3. 在服务端里提供一个 Service,由 Service 提供 Binder 实体:Stub 类继承自 Binder,这意味它本身就是个 Binder 实体,但是它是个抽象类,不提供 getMessage() 的实现。所以,需要我们继承 Stub,然后实现 getMessage()。最后,再创建一个 ExampleService(继承自 Service 类),然后在它的 onBind 里,返回我们的 Binder 实体。

    package com.example;
    
    
    
    public class ExampleService extends Service {
        private final IExampleService.Stub mBinder = new IExampleService.Stub() {
            @Override
            public String getMessage() {
                return "Hello from ExampleService!";
            }
    
        };
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    }
    

    当然,不要忘了在服务端的 AndroidManifest.xml 中注册服务:

    <service android:name=".ExampleService"
        android:exported="true"
        android:enabled="true">
        <intent-filter>
            <action android:name="com.example.IExampleService"/>
        </intent-filter>
    </service>
    
  4. 客户端通过 AIDL ,调用服务端里的 getMessage():

    先在客户端进程中,bindService() 绑定服务端的 ExampleService 服务。例如:

    private IExampleService mExampleService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mExampleService = IExampleService.Stub.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName className) {
            mExampleService = null;
        }
    };
    
    void bindService() {
        Intent intent = new Intent("com.example.IExampleService");
        intent.setPackage("com.example");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    
    void unbindService() {
        if (mExampleService != null) {
            unbindService(mConnection);
            mExampleService = null;
        }
    }
    

    这样,在客户端进程可以通过 AIDL 生成的 Proxy 的 getMessage(),调用服务端的 getMessage():

    try {
        String message = mExampleService.getMessage();
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    
    

Stub 和 Proxy

在 AIDL 自动生成的 IExampleService.java里,最关键的是 Stub 和 Proxy。它们之间的关系如下图:

Proxy 通常用于客户端,最关键的是它的 getMessage() :

public String getMessage() throws android.os.RemoteException {
    // _data 是写缓冲区
    android.os.Parcel _data = android.os.Parcel.obtain();
    // _reply 是读缓冲区
    android.os.Parcel _reply = android.os.Parcel.obtain();
    String _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        // TRANSACTION_getMessage 是函数索引
        // mRemote 是 BinderProxy 的实例
        boolean _status = mRemote.transact(Stub.TRANSACTION_getMessage, _data, _reply, 0);
        if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getMessage();
        }
        _reply.readException();
        // 从读缓冲区读取远程调用的结果
        _result = _reply.readString();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

Stub 通常用于服务端。Stub 继承自 Binder,代表它就是一个 Binder 实体。它是一个抽象类,不会实现 getMessage(),交给子类去实现。我们需要留意一下它的 onTransact():

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
    String descriptor = DESCRIPTOR;
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(descriptor);
            return true;
        }

        // 目标函数的索引
        case TRANSACTION_getMessage: {
            data.enforceInterface(descriptor);
            // 调用 getMessage()
            String _result = this.getMessage();
            reply.writeNoException();
            // 返回结果给客户端
            reply.writeString(_result);
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

在一次 getMessage() 的跨进程调用里,它们的作用如下:

image.png

transact()

transact() 会向 Binder 驱动发起事务,大致的调用链路是:

BinderProxy.java

└─transact()

└──transactNative()

└───android_util_Binder.cpp.cpp

└────android_os_BinderProxy_transact()

└─────BpBinder.cpp

└──────transact()

└───────IPCThreadState.cpp

└────────self()

└─────────transact()

└──────────writeTransactionData()

└──────────waitForResponse()

└───────────talkWithDriver()

└────────────ioctl.cpp

└─────────────ioctl()

注意:从 transact() 到 ioctl() ,这个过程都是同步调用。即当我们通过 AIDL 调用远程服务时,客户端会等待服务端完成处理并返回结果。整个过程中,客户端线程会阻塞,直到收到来自服务端的回应。所以,我们千万别在主线程发起调用。

onTransact()

服务端的 Binder 线程池的线程接收到 BR_TRANSACTION 消息后,会一路调用到 onTransact():

IPCThreadState.cpp

└─executeCommand() // 读取服务端的读缓冲区数据,处理 BR_TRANSACTION 消息

└──Binder.cpp

└───BBinder::transact() // 根据事务数据里的函数索引、参数,完成函数调用。

└────JavaBBinder.cpp(继承自 BBinder)

└─────onTransact()

└──────Binder.java

└───────execTransact()

└────────execTransactInternal()

└─────────onTransact() // Stub 会覆盖父类的 onTransact()

BpBinder 与 BinderProxy、BBinder 与 Binder

transact() 和 onTransact() 的调用链路里出现了 BpBinder、BinderProxy、BBinder(JavaBBinder)和 Binder。

在 Binder 事务一文,已经提到过,BBinder 即 Binder 实体,而 BpBinder 即 Binder 代理,持有着 Binder 引用。它们两个都是 C++ 里的类。

而 Binder、BinderProxy 则是它们的 Java 层表示。如下图:

应用级服务

服务端提供的 ExampleService 属于应用级服务。在前面的示例中,我们在客户端里会通过 bindService() 启动它,并在 ServiceConnection 回调里,拿到了所需的 Binder 引用。

应用级别的服务通常不会注册到 ServiceManager,而是由 AMS 进行管理。

当客户端发起 bindService() 调用后,会通过 Binder 调用到 AMS 的 bindServiceLocked(),进行服务的绑定。如果服务还没启动,AMS 会通过 Binder 通信,通知服务端创建、启动和绑定服务。服务端执行绑定逻辑后,会传递 Binder 实体给 Binder 驱动,Binder 驱动转换为 Binder 引用后,再传给 AMS。最后由 AMS 转交给客户端,即最终会执行 ServiceConnection 回调的 onServiceConnected()。

image.png

  • Binder 实体是服务端里的 IExampleService.Stub mBinder。
  • Binder 引用均是经过 Binder 驱动转换的,上图没有画出 Binder 驱动。

aidl-cpp

C++ 层也可以通过 aidl-cpp 来使用 AIDL。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYrLU7zY' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片