没掌握这些知识点也敢说自己熟悉AIDL?
AIDL介绍
AIDL的官方名称是Android Interface Definition Language(安卓接口定义语言),它在Android开发中被广泛应用于实现跨进程通信的一种方式。
由AIDL语言编写的代码生成的文件被称为aidl文件。在Android开发环境中, aidl文件本身并不直接参与执行。相反,在运行时阶段是由Android SDK工具通过解析aidl文件生成相应的Java IBinder接口。这表明你可以根据需求自定义IBinder接口以模拟AIDL功能的效果。
AIDL的使用
Server端
创建.aidl文件
package com.example.android
interface IRemoteService {
void setBookName(String name);
int getBookId();
}
AIDL encompasses a variety of data types. It includes Java's primitive data types, such as integers (int, long), characters (char), booleans (boolean), and their respective variants. Additionally, it supports the String type, character sequences, lists, and maps.
实现接口
创建Binder类的实例,实现aidl中的方法
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getBookId(){
return 1;
}
public void setBookName(String aString) {
// Does nothing
}
};
向客户端公开接口
通过Service的onBind方法,向客户端返回Binder对象。
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return binder;
}
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getBookId(){
return 1;
}
public void setBookName(String aString) {
// Does nothing
}
};
}
Client端
- 将客户端的AIDL文件导入项目环境中。
- 基于AIDL生成的一个IBinder接口实例被声明出来。
- 一个ServiceConnection接口被成功实现。
- 通过调用Context.bindService()方法来注入您的ServiceConnection实现。
- 在onServiceConnected方法体内返回一个IBinder实例(命名为service)。
- 通过调用服务器提供的相关方法来完成操作。
public static class Binding extends Activity {
IRemoteService mService = null;
private boolean isBound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.btn1);
button.setOnClickListener(mSetListener);
button = (Button)findViewById(R.id.btn2);
button.setOnClickListener(mGetListener);
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
mService = IRemoteService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
private OnClickListener mSetListener = new OnClickListener() {
public void onClick(View v) {
mService.setBookName("1988");
}
};
private OnClickListener mGetListener = new OnClickListener() {
public void onClick(View v) {
mService.getBookId();
}
};
}
AIDL通信的过程

Client利用ServiceConnection访问Server上的Binder并将其包装为Proxy。
Proxy被用来同步调用IPC方法。
通过Parcel传输参数给Binder。
Binder的transact方法最终触发了Server上的Stub的onTransact方法。
当Server返回结果后,Client从Parcel接收返回值。
从而完成了对IPC的一次调用。
AIDL关键字
通过实参得以顺利传递到服务端方,在此情况下即使实参发生任何变化也不会反馈给调用端。
在设计中,并没有将实参真正传递到服务方;相反地,在这种情况下,并不直接将实参传递给服务方;相反地,并未将整个参数进行复制或传递到对方;相反地,则是通过某种机制实现参数的安全隔离和访问控制。其中real参数仅用于作为返回值来处理;具体来说,在这种机制下,并非所有real参数都会被最终使用;例如,在return处得到处理后还能继续处理其他额外的内容。然而,在这种机制下,并非所有的real参数都会被最终使用;即使是在return处得到处理后还可以继续处理其他额外的内容。然而,在这种机制下,并非所有的real参数都会被最终使用;即使是在return处得到处理后还可以继续处理其他额外的内容。然而,在这种机制下,并非所有的real参数都会被最终使用;即使是在return处得到处理后还可以继续处理其他额外的内容。
输入输出混合参数则为上述两种类型的综合体现;实参与将顺畅地传递至服务端,并且在函数调用完成之后,在服务端一旦发生对实参与的任何修改,在函数调用完成之后会立即反馈给调用端。
它们都基于服务方进行操作。其中'in'表示数据进入系统的方式,'out'则表示数据离开系统的方式
- oneway
每个普通的AIDL函数都会产生一个返回值;若未预先声明则函数会返回void。当调用该方法时,在当前线程中必须等待函数执行完毕后才能继续其他操作。
而通过一个oneway标识符标记一个AIDL方法后,该方法将不再返回任何值;仅仅通过binder来通知server执行此操作.相应的线程不会阻塞等待该操作完成.
同时, Oneway's meaning mirrors its translation. A road, or a linear pathway, the binder will sequentially execute each call.
AIDL生成的文件
Android SDK工具会根据AIDL创建Java语言的接口文件。

该接口继承自android.os.IInterface。该接口包含三个重要的嵌套类:Default类(图中未展示)和Stub类以及基于AIDL所声明的方法。这些方法明确说明了其功能和使用方法,并且支持通过生成代码的方式实现跨平台开发。
Default类
Default类是实现了接口的类,是接口的默认实现。
Stub类
Stub作为抽象类,在Android系统框架提供的基础组件之一android.os.Binder下进行延伸开发,并提供了多个服务类型间的交互接口支持。在Service类中定义的onBind方法应返回Stub的实际子类以完成绑定操作。
DESCRIPTOR
Binder具有独特的标识性。
不同进程间需借助序列化的DESCRIPTOR以定位对应的Binder。
同样地,在相同进程中也需要利用DESCRIPTOR来获取对应的Binder。
TRANSACTION_testFunction
使用IPC方法的唯一标识符。
Stub.onTransact()中会通过传递TRANSACTION_testFunction来执行testFunction()的方法。
当Proxy调用transact时, 也会携带TRANSACTION_testFunction以标识其需要执行的具体逻辑。
asInterface()
主要针对服务端的IBinder对象进行转换操作以生成ITestServer对象。当服务器进程与客户端进程相同时,则能够通过queryLocalInterface(DESCRIPTOR)定位Binder所返回的对象这一结果;而当服务器进程与客户端处于不同进程时,则将返回一个经过封装处理后的Proxy实例。
asBinder()
返回Stub自己。
onTransact()
IPC接口被调用的逻辑:
- 提取数据中的必要参数
- 将提取的数据参数输入至服务器端处理流程中,并获取相应的处理结果。
- 将处理所得的结果信息输出至响应字段中。
Proxy
Proxy是Stub的内部类,实现了接口。
asBinder()
Proxy在asBinder的时候会返回server的IBinder。
testFunction()
该测试函数被Proxy执行,其背后机制实际上是通过mRemote.transact()方法导致远端Stub的onTransact方法被激活。
调用的步骤如下:
- 建立一个包含参数和返回值的Parcel对象容器,并将参数存储于其中。
- 通过调用mRemote.transact()方法获取计算所得的结果会被存储在该(Parcel)实例中。
- 通过解析(Parcel对象中的)结果获取相应的返回值并将之传递给调用方。
AIDL回调接口
和普通的接口一样,AIDL定义的接口方法也可以接受一个callback对象来作为参数传递.这个Callback对象必须先在AIDL中进行定义.
IValueChangeListener.aidl
interface IValueChangeListener {
void onValueChanged(boolean value);
}
之后在方法中使用
IRemoteService.aidl
package com.android.test;
import com.android.test.IValueChangeListener;
interface IRemoteService {
void registerListener(IValueChangeListener listener);
void unregisterListener(IValueChangeListener listener);
}
不同的是,在其他对象或参数通过Parcel的形式进行传输时,在aidl接口作为参数被传递时实际上是指向另一个系统进行了Binder的传输。
在app/build/generated/aidl_source_output_dir/debug/out/com/android/test目录下除了能识别IRemoteService接口之外,另外还能发现IValue Changesteller 接口的存在,并且其内部架构与IRemoteService相同。
另外在IRemoteService.Stub的onTransact方法中可以看到
data.enforceInterface(descriptor);
com.android.test.IValueChangeListener _arg0;
_arg0 = com.android.test.IValueChangeListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
Server负责处理Binder对象转换为Proxy的任务。在执行callback方法时, Server与Client之间实现了角色切换, 作为客户端参与通信的服务器转变为了客户端角色, 同时原来的客户端则转变为服务器角色进行操作。
其他
参数和结果为什么读写都需要通过Parcel?
进程间通信因资源互不共享的原因,在无法直接传输对象时(需)须借助序列化技术,在不同内存空间中复制两份完全相同的资源。然而(Parcel)作为一种实现手段,在实际应用中得到了广泛使用。
AIDL开发优化
一个Service返回给Client不同的Binder
通常情况下,在服务设计阶段, 我们倾向于为客户端呈现多种解决方案以满足不同的使用场景. 而将所有的功能需求全部集中在同一个AI DSL (Domain Specific Language) 文件中, 显然会导致高度的耦合性问题. 一旦有任何功能需求需要更新维护, 则整个AI DSL文档都会受到影响.
一种合理的做法是将同一功能的方法整合到同一个aidl文件中。这也就意味着将单一功能的aidl文件按照功能划分成多个相互独立的部分。那么问题就出现了:每一个aidl接口文件都与对应的Binder实现相关联。然而,在每个onBind方法中只能返回一个Binder实例。这就引发了疑问:是否需要创建多个Service?每个Service在其onBind方法中负责提供不同的Binder实例?
这种做法在面对少数几个Service时表现尚可,但当有10个aidl需要处理时(即当需要创建多达10个这样的服务时),资源消耗急剧增加。
在这样的情况下,在这种特定的场景下可以通过引入一种新的 aidl 方案来实现这一目标 也就是说 通过采用一种 aidl 方法 在这种情况下 返回不同的 binder 的方式 并且 以便于该服务能够返回多个不同类型的 aidl 这样一来 就能够使得该服务能够支持多种类型的一阶逻辑推理系统
具体流程:
- 生成不同的AIDL文件,并配置相应的功能模块。
- 生成不同的Binder类,并基于各个AIDL接口的Stub类进行设计与配置相应的功能逻辑。
- 设计一个main AIDL文件,在其中定义queryBinder方法,并接受参数为BINDER_CODE的形式(.NET IBinder实例)。
- 基于前面设计的基础之上, 重构该method以实现根据不同BINDER_CODE选择相应Binder对象的功能。
- 创建AService实例,在onBind事件中, 将当前main Binder对象作为返回值传递给调用者。
通过版本标识返回Client不同的Binder
开发AIDL时需确保客户端与服务端的有效协作。每当服务端更新其提供的AIDL接口时, 客户端必须同步做出相应的调整。然而, 在某些情况下由于信息传递不够及时或沟通不够顺畅, 在这种情况下即使服务端进行了接口的修改或升级
当前阶段,在进行Service绑定操作时
