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) 客户端界面
<?xml version="1.0" encoding="utf-8"?> <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); } }先启动服务端,再启动客户端,输入参数进行计算