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();
}
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