refs: http://developer.android.com/guide/components/aidl.html
AIDL(Android Interface Definition Language) 就像其它接口定义语言一样。它使你可以定义服务端及客户端程序的接口,以达到跨进程沟通( IPC )的目的。
注意:
1。在多进程多线程的情况下,我们才使用 AIDL
2。单一进程时,使用实现 Binder 类的方式定义接口
3。如果只有跨进程,但不需処理多线程的情况,请使用 Messenger
一、创造一个 AIDL 档案
// IMyAidlInterface.aidl package com.example.shanwu.interprocesscomm; // Declare any non-default types here with import statements interface IMyAidlInterface { int getPid(); String getServiceName(); void makeSingingService(); }
AIDL 支持以下数据类型做为接口方法的参数与回传类型:
1. 所有 primitive data type
2. String
3. CharSequence
4. List
5. Map
将 aidl 档存放於 /src 下,在编译过程,其会自动生成一个 .java 档,包括了一个名为 Stub 的子类,其为一个 aidl接口类的 abstract implmentation,并且有著所有 aidl 接口类的所有宣告方法如下,这些我们在後面会一一讲解,使我们具有自己实现,而不依赖 aidl 的能力,如下:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /home/shanwu/GitHub/InterProcessCommPractice/InterProcessComm/app/src/main/aidl/com/example/shanwu/interprocesscomm/IMyAidlInterface.aidl */ package com.example.shanwu.interprocesscomm; // Declare any non-default types here with import statements public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.shanwu.interprocesscomm.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.example.shanwu.interprocesscomm.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.shanwu.interprocesscomm.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.shanwu.interprocesscomm.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.shanwu.interprocesscomm.IMyAidlInterface))) { return ((com.example.shanwu.interprocesscomm.IMyAidlInterface) iin); } return new com.example.shanwu.interprocesscomm.IMyAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @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_getPid: { data.enforceInterface(DESCRIPTOR); int _result = this.getPid(); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_getServiceName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getServiceName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_makeSingingService: { data.enforceInterface(DESCRIPTOR); this.makeSingingService(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.shanwu.interprocesscomm.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; } @Override public int getPid() 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); mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.lang.String getServiceName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getServiceName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void makeSingingService() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_makeSingingService, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getServiceName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_makeSingingService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); } public int getPid() throws android.os.RemoteException; public java.lang.String getServiceName() throws android.os.RemoteException; public void makeSingingService() throws android.os.RemoteException; }
Stub 也定义了一些辅助方法,该特别注意的是 asInterface(),其拿一个 IBinder 作为参数(通常是传进客户端的 onServiceConnected()的回调方法)并返回一个 stub 接口对象。
二、实现接口
我们需要实现 aidl 产生的接口,范例如下:
private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @Override public int getPid() throws RemoteException { return android.os.Process.myPid(); } @Override public String getServiceName() throws RemoteException { return "Worker Service~ "; } @Override public void makeSingingService() { Log.d("guang", "singing~~~in log..."); //Toast.makeText(WorkerService.this, "singing~~~", Toast.LENGTH_LONG).show(); not gonna work for the exception mHandler.sendEmptyMessage(MSG_START_SING); } };
现在 mBinder 是一个 Stub 类的实例,并且实现了接口。下一步,这个实例将暴露给客户端,以使他们能够和 Service 互动。在实现 aidl 接口的时候,我们需要注意以下几点:
1. 因为命令不见得是在主线程上执行,所以必须考量多线程的情况,该 Service 得是线程安全。
2. RPC命令一般缺省的情况是同步的。如果 Service 会花一些时间処理一个 request 的话,便不应从主线程呼叫,否则会产生 ANR。
3. 没有任何的异常会回传给呼叫方 (caller)
三、暴露接口给给客户端使用
当客户端呼叫 bindService() 以建立连结时,onServiceConntected回调会接收到 Service onBind()所返回的 mBinder 实例。如果客端是在不同的应用,则客户端也要有一份 aidl 档案在 src/ 路径里。当客户端在 onServiceConnected()回调接收到 IBinder後,我们必须调用 AIDL接口类.Stub.asInterface(service),并将其返回值强转为我们的 AIDL接口类名,如下:
public class MainActivity extends Activity implements View.OnClickListener { private ServiceConnection mServiceConn; private IMyAidlInterface mService; private boolean mIsBind = false; private int mWorkerId = -1;
private void initData() { mServiceConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mService = IMyAidlInterface.Stub.asInterface(iBinder); Log.d("guang", "onServiceConnected"); mIsBind = true; } @Override public void onServiceDisconnected(ComponentName componentName) { Log.d("guang", "onServiceDisconnected"); mIsBind = false; } }; }然後就可以从客户端呼叫服务端的 Service 了
case R.id.btn_sing: if (mIsBind) { try { mWorkerId = mService.getPid(); Toast.makeText(MainActivity.this, mWorkerId + "", Toast.LENGTH_LONG).show(); mService.makeSingingService(); } catch (RemoteException e) { Log.e("guang", "Service is dead " + e); } } break;
完整例子:
https://github.com/shanwu/InterProcessCommPractice/tree/ipc_aidl_example