🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
经过上一节的介绍,读者已经明白在Java层Binder的架构中,Bp端可以通过BinderProxy的transact()方法与Bn端发送请求,而Bn端通过继承Binder类并重写onTransact()接收并处理来自Bp端的请求。这个结构非常清晰而且简单,但是实现起来却颇为繁琐。于是Android提供了AIDL语言以及AIDL解释器自动生成一个服务的Bn端即Bp端的用于处理Binder通信的代码。 AIDL的语法与定义一个Java接口的语法非常相似。为了避免业务实现对分析的干扰,本节通过一个最简单的例子对AIDL的原理进行介绍: **IMyServer.aidl** ``` package com.understanding.samples; interface IMyServer { intfoo(String str); } ``` IMyServer.aidl定义了一个名为IMyServer的Binder服务,并提供了一个可以跨Binder调用的接口foo()。可以通过aidl工具将其解析为一个实现了Bn端及Bp端通过Binder进行通信的Java源代码。具体命令如下: ``` aidl com/understanding/samples/IMyServer.aidl ``` 生成的IMyServer.java可以在com/understanding/samples/文件夹下找到。 * * * * * **建议** 读者可以阅读aidl有关的文档了解此工具的详细功能。 * * * * * **IMyServer.java-->IMyServer** ``` package com.understanding.samples; /* **① 首先,IMyServer.aidl被解析为一个Java接口IMyServer。**这个接口定义了AIDL文件中 所定义的接口foo() */ public interface IMyServer extendsandroid.os.IInterface { /***② aidl工具生成了一个继承自IMyServer接口的抽象类IMyServer.Stub。**这个抽象类实现了 Bn端通过onTransact()方法接收来自Bp端的请求的代码。本例中的foo()方法在这个类中 会被定义成一个抽象方法。因为aidl工具根不知道foo()方法是做什么的,它只能在onTransact() 中得知Bp端希望对foo()方法进行调用,所以Stub类是抽象的。 */ publicstatic abstract class Stub extends android.os.Binder implements com.understanding.samples.IMyServer{ ...... // Stub类的其他实现 /*onTransact()根据code的值选择调用IMyServer接口中的不同方法。本例中 TRANSACTION_foo意味着需要通过调用foo()方法完成请求 */ public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { ...... case TRANSACTION_foo: { ...... // 从data中读取参数_arg0 // Stub类的子类需要实现foo()方法 int _result = this.foo(_arg0); ...... // 向reply中写入_result return true; } } return super.onTransact(code, data, reply, flags); } /* **③ aidl工具还生成了一个继承自IMyServer接口的类Proxy,它是Bp端的实现。**与Bn端的 Stub类不同,它实现了foo()函数。因为foo()函数在Bp端的实现是确定的,即将参数存储到 Parcel中然后执行transact()方法将请求发送给Bn端,然后从reply中读取返回值并返回 给调用者 */ private static class Proxy implements com.understanding.samples.IMyServer{ ...... // Proxy类的其他实现 public int foo(java.lang.String str) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { ...... // 将参数str写入参数_data // mRemote就是指向IMyServer Bn端的BinderProxy mRemote.transact(Stub.TRANSACTION_foo, _data, _reply, 0); ......// 从_replay中读取返回值_result } finally { ...... } return _result; } } // TRANSACTION_foo常量用于定义foo()方法的code static final int TRANSACTION_foo = (android.os.IBinder.FIRST_CALL_TRANSACTION+ 0); } // 声明IMyServer所提供的接口 publicint foo(java.lang.String str) throws android.os.RemoteException; } ``` 可见一个AIDL文件被aidl工具解析之后会产生三个物件: - IMyServer接口。它仅仅用来在Java中声明IMyServer.aidl中所声明的接口。 - IMyServer.Stub类。这个继承自Binder类的抽象类实现了Bn端与Binder通信相关的代码。 - IMyServer.Stub.Proxy类。这个类实现了Bp端与Binder通信相关的代码。 在完成了aidl的解析之后,为了实现一个Bn端,开发者需要继承IMyServer.Stub类并实现其抽象方法。如下所示: ``` class MyServer extends IMyServer.Stub { intfoo(String str) { // 做点什么都可以 returnstr.length(); } } ``` 于是每一个MyServer类的实例,都具有了作为Bn端的能力。典型的做法是将MyServer类的实例通过ServiceManager.addService()将其注册为一个系统服务,或者在一个Android标准Service的onBind()方法中将其作为返回值使之可以被其他进程访问。另外,也可以通过Binder调用将其传递给另外一个进程,使之成为一个跨进程的回调对象。 那么Bp端将如何使用IMyServer.Proxy呢?在Bp端所在进程中,一旦获取了IMyServer的BinderProxy(通过ServiceManager.getService()、onServiceConnected()或者其他方式),就可以以如下方式获得一个IMyServer.Proxy: ``` // 其中binderProxy就是通过ServiceManager.getService()所获取 IMyServer remote = IMyServer.Stub.asInterface(binderProxy); remote.foo(“Hello AIDL!”); IMyServer.Stub.asInterface()的实现如下: [IMyServer.java-->IMyServer.Stub.asInterface()] public static com.understanding.samples.IMyServerasInterface( android.os.IBinder obj) { ...... // 创建一个IMyServer.Stub.Proxy。其中参数obj将会被保存为Proxy类的mRemote成员。 return new com.understanding.samples.IMyServer.Stub.Proxy(obj); } ``` 可见,AIDL使得构建一个Binder服务的工作大大地简化了。