本文将以一个例子来和你分享使用AIDL的基础技能,这个例子里有:
1、一个类mAIDLActivity,继承Activity。里面有三个按钮,text分别为StartService,StopService,CallbackTest。
2、一个类mAIDLService,继承Service。为了充分展示ADIL的功能,它做以下工作:当用户点击CallbackTest按钮时,从mAIDLActivity调用mAIDLService中的Stub 对象的一个方法invokCallBack(),而这个方法又会调用mAIDLActivity中Stub 对象的一个方法performAction(),这个方法在屏幕上显示一个toast。没什么意义,只是展示一下AIDL如何使用。
3、两个AIDL文件:forService.aidl和forActivity.aidl。对应名字,在Service和Activity中分别有对象需要用到它们定义的接口。
开发环境为Eclipse。
拣重要的先说,来看看aidl文件的内容:
文件:forActivity.aidl
- package
com.styleflying.AIDL; - interface
forActivity { -
void performAction(); -
}
文件:forService.aidl
- package
com.styleflying.AIDL; - import
com.styleflying.AIDL.forActivity; - interface
forService { -
void registerTestCall(forActivity cb); -
void invokCallBack(); - }
这两个文件和Java文件放置的地方一样,看包名。
在Eclipse中它们将被自动编译为forActivity.java和forService.java,它们存放在gen目录下。为了方便手头无法演练的读者,代码贴上,不用细看。
文件forActivity.java:
-
- package
com.styleflying.AIDL; - import
java.lang.String; - import
android.os.RemoteException; - import
android.os.IBinder; - import
android.os.IInterface; - import
android.os.Binder; - import
android.os.Parcel; - public
interface forActivity extends android.os.IInterface - {
-
- public
static abstract class Stub extends android.os.Binder implements com.styleflying.AIDL.forActivity - {
- private
static final java.lang.String DESCRIPTOR = "com.styleflying.AIDL.forActivity"; -
- public
Stub() - {
- this.attachInterface(this,
DESCRIPTOR); - }
-
- public
static com.styleflying.AIDL.forActivity asInterface(android.os.IBinder obj) - {
- if
((obj==null)) { - return
null; - }
- android.os.IInterface
iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); - if
(((iin!=null)&&(iin instanceof com.styleflying.AIDL.forActivity))) { - return
((com.styleflying.AIDL.forActivity)iin); - }
- return
new com.styleflying.AIDL.forActivity.Stub.Proxy(obj); - }
- 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_performAction: - {
- data.enforceInterface(DESCRIPTOR);
- this.performAction();
- reply.writeNoException();
- return
true; - }
- }
- return
super.onTransact(code, data, reply, flags); - }
- private
static class Proxy implements com.styleflying.AIDL.forActivity - {
- private
android.os.IBinder mRemote; - Proxy(android.os.IBinder
remote) - {
- mRemote
= remote; - }
- public
android.os.IBinder asBinder() - {
- return
mRemote; - }
- public
java.lang.String getInterfaceDescriptor() - {
- return
DESCRIPTOR; - }
- public
void performAction() 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_performAction,
_data, _reply, 0); - _reply.readException();
- }
- finally
{ - _reply.recycle();
- _data.recycle();
- }
- }
- }
- static
final int TRANSACTION_performAction = (IBinder.FIRST_CALL_TRANSACTION + 0); - }
- public
void performAction() throws android.os.RemoteException; - }
文件forService.java:
-
- package
com.styleflying.AIDL; - import
java.lang.String; - import
android.os.RemoteException; - import
android.os.IBinder; - import
android.os.IInterface; - import
android.os.Binder; - import
android.os.Parcel; - public
interface forService extends android.os.IInterface - {
-
- public
static abstract class Stub extends android.os.Binder implements com.styleflying.AIDL.forService - {
- private
static final java.lang.String DESCRIPTOR = "com.styleflying.AIDL.forService"; -
- public
Stub() - {
- this.attachInterface(this,
DESCRIPTOR); - }
-
- public
static com.styleflying.AIDL.forService asInterface(android.os.IBinder obj) - {
- if
((obj==null)) { - return
null; - }
- android.os.IInterface
iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); - if
(((iin!=null)&&(iin instanceof com.styleflying.AIDL.forService))) { - return
((com.styleflying.AIDL.forService)iin); - }
- return
new com.styleflying.AIDL.forService.Stub.Proxy(obj); - }
- 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_registerTestCall: - {
- data.enforceInterface(DESCRIPTOR);
- com.styleflying.AIDL.forActivity
_arg0; - _arg0
= com.styleflying.AIDL.forActivity.Stub.asInterface(data.readStrongBinder()); - this.registerTestCall(_arg0);
- reply.writeNoException();
- return
true; - }
- case
TRANSACTION_invokCallBack: - {
- data.enforceInterface(DESCRIPTOR);
- this.invokCallBack();
- reply.writeNoException();
- return
true; - }
- }
- return
super.onTransact(code, data, reply, flags); - }
- private
static class Proxy implements com.styleflying.AIDL.forService - {
- private
android.os.IBinder mRemote; - Proxy(android.os.IBinder
remote) - {
- mRemote
= remote; - }
- public
android.os.IBinder asBinder() - {
- return
mRemote; - }
- public
java.lang.String getInterfaceDescriptor() - {
- return
DESCRIPTOR; - }
- public
void registerTestCall(com.styleflying.AIDL.forActivity cb) throws android.os.RemoteException - {
- android.os.Parcel
_data = android.os.Parcel.obtain(); - android.os.Parcel
_reply = android.os.Parcel.obtain(); - try
{ - _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));
- mRemote.transact(Stub.TRANSACTION_registerTestCall,
_data, _reply, 0); - _reply.readException();
- }
- finally
{ - _reply.recycle();
- _data.recycle();
- }
- }
- public
void invokCallBack() 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_invokCallBack,
_data, _reply, 0); - _reply.readException();
- }
- finally
{ - _reply.recycle();
- _data.recycle();
- }
- }
- }
- static
final int TRANSACTION_registerTestCall = (IBinder.FIRST_CALL_TRANSACTION + 0); - static
final int TRANSACTION_invokCallBack = (IBinder.FIRST_CALL_TRANSACTION + 1); - }
- public
void registerTestCall(com.styleflying.AIDL.forActivity cb) throws android.os.RemoteException; - public
void invokCallBack() throws android.os.RemoteException; - }
两段代码差不多,前面基本一样,从后面看,最后跟着我们在AIDL中自定义的方法,没有实现。两个文件各定义一个了接口,这两个接口分别会在Activity和Service中使用,在那里我们将实现自定义的方法。两个接口中都定义了一个抽象类Stub,实现所在的接口。Stub中又有一个类Proxy。Stub中有一个static的asInterface()方法,里面有很多return语句,在mAIDLActivity中调用它时,它返回一个新创建的内部类Proxy对象。
这个Stub对我们来说很有用,它继承了Binder。Binder有什么用呢?一个类,继承了Binder,那么它的对象就可以被远程的进程使用了(前提是远程进程获取了这个类的对象【对象的引用】,至于如如何获得看下文),在本例中就是说,如果一个Service中有一个继承了Stub的类的对象,那么这个对象中的方法就可以在Activity中使用,对Activity也是这样。至于Binder的细节,网上有很多贴介绍,看不明白也不影响我们完成这个例子。
再看mAIDLActivity.java:
- package
com.styleflying.AIDL; - import
android.app.Activity; - import
android.content.ComponentName; - import
android.content.Context; - import
android.content.Intent; - import
android.content.ServiceConnection; - import
android.os.Bundle; - import
android.os.IBinder; - import
android.os.RemoteException; - import
android.util.Log; - import
android.view.View; - import
android.view.View.OnClickListener; - import
android.widget.Button; - import
android.widget.Toast; - public
class mAIDLActivity extends Activity { -
private static final String TAG = "AIDLActivity"; -
private Button btnOk; -
private Button btnCancel; -
private Button btnCallBack; -
-
private void Log(String str) { -
Log.d(TAG, "------ " + str + "------"); -
} -
-
private forActivity mCallback = new forActivity.Stub() { -
public void performAction() throws RemoteException -
{ -
Toast.makeText(mAIDLActivity.this, "this toast is called from service", 1).show(); -
} -
}; -
-
forService mService; -
private ServiceConnection mConnection = new ServiceConnection() { -
public void onServiceConnected(ComponentName className, -
IBinder service) { -
mService = forService.Stub.asInterface(service); -
try { -
mService.registerTestCall(mCallback);} -
catch (RemoteException e) { -
-
} -
} -
public void onServiceDisconnected(ComponentName className) { -
Log("disconnect service"); -
mService = null; -
} -
}; -
@Override -
public void onCreate(Bundle icicle) { -
super.onCreate(icicle); -
setContentView(R.layout.main); -
btnOk = (Button)findViewById(R.id.btn_ok); -
btnCancel = (Button)findViewById(R.id.btn_cancel); -
btnCallBack = (Button)findViewById(R.id.btn_callback); -
btnOk.setOnClickListener(new OnClickListener() { -
public void onClick(View v) { -
Bundle args = new Bundle(); -
Intent intent = new Intent(mAIDLActivity.this, mAIDLService.class); -
intent.putExtras(args); -
bindService(intent, mConnection, Context.BIND_AUTO_CREATE); -
startService(intent); -
} -
}); -
btnCancel.setOnClickListener(new OnClickListener() { -
public void onClick(View v) { -
unbindService(mConnection); -
//stopService(intent); -
} -
}); -
btnCallBack.setOnClickListener(new OnClickListener() { -
-
@Override -
public void onClick(View v) -
{ -
try -
{ -
mService.invokCallBack(); -
} catch (RemoteException e) -
{ -
// TODO Auto-generated catch block -
e.printStackTrace(); -
} -
} -
}); -
} - }
很短,相信大家很容易看明白。注意mConnection,它的onServiceConnected()中有一句mService = forService.Stub.asInterface(service);给mService赋值了,这个mService是一个forService,而service是onServiceConnected()传进来的参数,onServiceConnected()会在连接Service的时候被系统调用,这个service参数的值来自哪里呢?看mAIDLService.java:
- package
com.styleflying.AIDL; - import
android.app.Service; - import
android.content.Intent; - import
android.os.IBinder; - import
android.os.RemoteCallbackList; - import
android.os.RemoteException; - import
android.util.Log; - public
class mAIDLService extends Service { -
private static final String TAG = "AIDLService"; -
private forActivity callback; -
private void Log(String str) { -
Log.d(TAG, "------ " + str + "------"); -
} -
@Override -
public void onCreate() { -
Log("service create"); -
} -
@Override -
public void onStart(Intent intent, int startId) { -
Log("service start id=" + startId); -
} -
-
@Override -
public IBinder onBind(Intent t) { -
Log("service on bind"); -
return mBinder; -
} -
@Override -
public void onDestroy() { -
Log("service on destroy"); -
super.onDestroy(); -
} -
@Override -
public boolean onUnbind(Intent intent) { -
Log("service on unbind"); -
return super.onUnbind(intent); -
} -
public void onRebind(Intent intent) { -
Log("service on rebind"); -
super.onRebind(intent); -
} -
private final forService.Stub mBinder = new forService.Stub() { -
@Override -
public void invokCallBack() throws RemoteException -
{ -
callback.performAction(); -
-
} -
@Override -
public void registerTestCall(forActivity cb) throws RemoteException -
{ -
callback = cb; -
-
} -
-
}; - }
注意onBind(),它的返回类型为IBinder,返回了一个mBinder,看看mBinder的定义:
它是实现了我们在AIDL中定义的方法,这个mBinder最终返回给了mAIDLActivity中的mService,于是在mAIDLActivity中可以使用mBinder中的方法了。在mAIDLActivity中也有一个类似mBinder的对象,看看定义:
我们要在界面上显示一个toast,就是在这里实现的。这个对象,在mConnection的onServiceConnected()被调用时,通过调用mService(也就是远程的mAIDLService中的mBinder)的registerTestCall(),传递给了mAIDLService,于是在mAIDLService中可以调用performAction()了。
做一个大致总结,我们使用AIDL是要做什么呢:
让Acticity(或者说一个进程/一个类?)和Service(或者说远端进程/远端类/对象?)获取对方的一个Stub对象,这个对象在定义时实现了我们在AIDL中定义的方法,于是这些远程对象中的方法可以在本地使用了。如果这种使用(通信)是单向的,比如只是Activity需要通知Service做什么,那么只要Service中有一个Stub对象,并且传给Acticity就够了。
至于如何获得远程的Stub,参看上面的代码,看mConnection、registerTestCall、onRebind,它们展示了一种方法。
另外,有时候我们可能在一个类中有多个Stub对象,它们都要给远程交互的类的实例,这个时候可以考虑使用RemoteCallbackList<>(docs/reference/android/os/RemoteCallbackList.html)。