AIDL,全称是Android Interface Define Language,即安卓接口定义语言,可以实现安卓设备中进程之间的通信(Inter Process Communication, IPC)。
假设有如下场景,需要计算a+b的值,在客户端中获取a和b的值,然后传递给服务端,服务端进行a+b的计算,并将计算结果返回给客户端。
这里的客户端和服务端均是指安卓设备中的应用,由于每一个应用对应一个进程,由此可以模拟安卓系统中通过AIDL进行进程之间的通信。
AIDL的具体使用步骤如下:
1) 创建Project,命名为AIDLDemo
2) 将android studio自动生成的app module作为服务端应用,在此Procject中再新建一个module作为客户端应用,命名为aidlclient。
3) 在两个module中均新建一个与java文件夹同级的文件夹,命名为aidl,用于存放aidl文件,然后再分别在aidl文件夹下新建包,注意,两个module的新建包包名必须相同,然后在两个包中分别新建一个同名的aidl文件。项目的最终结构如下图:
4) 编辑AIDL文件
在服务端和客户端的AIDL文件中均编写add(int num1, int num2)方法,无需具体实现
// IMyAidlInterface.aidl
package com.zheero.aidldemo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
int add(int num1, int num2);
}
5) 点击同步按钮,会自动生成一个与aidl对应的java文件,其实是一个接口类
在该文件中,可以找到一个proxy类,这是一个代理类,运行在客户端中,其实客户端与服务端没有进行直接通信,客户端调用的add方法是proxy中的,然后代理类proxy再与服务端进行通信,将服务端返回的结果返回给客户端。
proxy类的简单解析如下
//代理类,运行在客户端中
private static class Proxy implements com.zheero.aidldemo.IMyAidlInterface {
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;
}
//客户端调用此方法,传递进来num1和num2两个参数
@Override
public int add(int num1, int num2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//向_data中写入两个参数num1和num2
_data.writeInt(num1);
_data.writeInt(num2);
//通过transact方法向服务端传递参数,并调用了方法,返回的结果写入_reply中
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
//读取返回的结果
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
在该文件中,还有一个Stub类,运行在服务端中,其中的onTransact方法解析如下
//运行在服务端Binder线程池中
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
//读取客户端传过来的参数
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
//调用add方法对参数进行运算
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
//返回计算结果
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
6) 在服务端中新建一个类,继承Service,在其中定义一个IBinder类型的变量iBinder,引用上述接口类中的Stub类对象,实现其中的add方法,在Service的onBind方法中,返回iBinder变量。最终代码如下:
public class IRemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
//将接口暴露给客户端
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub(){
//实现aidl中定义的方法
@Override
public int add(int num1, int num2) throws RemoteException {
return num1 + num2;
}
};
}
注意在服务端的Manifast文件中注册该Service,在注册时应设置exported属性为true,保证该Service能被其他应用调用,否则会报
java.lang.SecurityException: Not allowed to bind to service Intent 异常。
<service android:name=".IRemoteService"
android:exported="true"/>
至此,与配置AIDL有关的工作完成
7) 客户端界面
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:gravity="center|right"
android:id="@+id/et_num1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="+"
android:textSize="24sp"
android:layout_gravity="center"
android:gravity="right|center"/>
<EditText
android:gravity="center|right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_num2"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="="
android:textSize="24sp"
android:layout_gravity="center"
android:gravity="right|center"/>
<EditText
android:gravity="center|right"
android:editable="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit_show_result"
android:textSize="24sp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_count"
android:text="远程计算"
android:gravity="center"
android:textSize="24sp" />
LinearLayout>
8) 客户端MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_num1;
private EditText et_num2;
private EditText edit_show_result;
private Button btn_count;
private int mNum1;
private int mNum2;
private int mTotal;
private IMyAidlInterface iMyAidlInterface;
private final String TAG = getClass().getSimpleName();
//绑定服务回调
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//服务绑定成功后调用,参数中的service即是在服务端onBind方法中返回的iBinder,即已实现的接口
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
Log.i(TAG, "onServiceConnected: iMyAidlInterface" + iMyAidlInterface);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//解除绑定时调用, 清空接口,防止内容溢出
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindService();
}
private void initView(){
et_num1 = (EditText) findViewById(R.id.et_num1);
et_num2 = (EditText) findViewById(R.id.et_num2);
edit_show_result = (EditText) findViewById(R.id.edit_show_result);
btn_count = (Button) findViewById(R.id.btn_count);
btn_count.setOnClickListener(this);
}
/**
* 绑定服务
*/
private void bindService(){
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.zheero.aidldemo"
, "com.zheero.aidldemo.IRemoteService"));
boolean isBind = bindService(intent, conn, Context.BIND_AUTO_CREATE);
Log.i(TAG, "bindService: isBind->"+isBind);
}
@Override
public void onClick(View v) {
mNum1 = Integer.parseInt(et_num1.getText().toString());
mNum2 = Integer.parseInt(et_num2.getText().toString());
try {
mTotal = iMyAidlInterface.add(mNum1, mNum2);
} catch (RemoteException e) {
e.printStackTrace();
}
edit_show_result.setText(mTotal+"");
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
先启动服务端,再启动客户端,输入参数进行计算