AIDL
在Android中,如果我們需要在不同進程間實現通信,就需要用到AIDL技術去完成。
AIDL(Android Interface Definition Language)是一種接口定義語言,編譯器通過*.aidl文件的描述信息生成符合通信協議的Java代碼,我們無需自己去寫這段繁雜的代碼,只需要在需要的時候調用即可,通過這種方式我們就可以完成進程間的通信工作。關于AIDL的編寫規則我在這里就不多介紹了,讀者可以到網上查找一下相關資料。
接下來,我就演示一個操作AIDL的最基本的流程。
首先,我們需要建立一個服務端的工程,如圖所以:
在IPerson.aidl中我們定義了一個“問候”的方法,代碼如下:
[java]view plaincopy
packagecom.scott.aidl;
interfaceIPerson{
Stringgreet(Stringsomeone);
}
在Eclipse插件的幫助下,編譯器會自動在gen目錄中生成對應的IPerson.java文件,格式化后的代碼如下:
[java]view plaincopy
packagecom.scott.aidl;
publicinterfaceIPersonextendsandroid.os.IInterface{
/**Local-sideIPCimplementationstubclass.*/
publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.scott.aidl.IPerson{
privatestaticfinaljava.lang.StringDESCRIPTOR="com.scott.aidl.IPerson";
/**Constructthestubatattachittotheinterface.*/
publicStub(){
this.attachInterface(this,DESCRIPTOR);
}
/**
*CastanIBinderobjectintoancom.scott.aidl.IPersoninterface,
*generatingaproxyifneeded.
*/
publicstaticcom.scott.aidl.IPersonasInterface(android.os.IBinderobj){
if((obj==null)){
returnnull;
}
android.os.IInterfaceiin=(android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if(((iin!=null)&&(iininstanceofcom.scott.aidl.IPerson))){
return((com.scott.aidl.IPerson)iin);
}
returnnewcom.scott.aidl.IPerson.Stub.Proxy(obj);
}
publicandroid.os.IBinderasBinder(){
returnthis;
}
@Override
publicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags)
throwsandroid.os.RemoteException{
switch(code){
caseINTERFACE_TRANSACTION:{
reply.writeString(DESCRIPTOR);
returntrue;
}
caseTRANSACTION_greet:{
data.enforceInterface(DESCRIPTOR);
java.lang.String_arg0;
_arg0=data.readString();
java.lang.String_result=this.greet(_arg0);
reply.writeNoException();
reply.writeString(_result);
returntrue;
}
}
returnsuper.onTransact(code,data,reply,flags);
}
privatestaticclassProxyimplementscom.scott.aidl.IPerson{
privateandroid.os.IBindermRemote;
Proxy(android.os.IBinderremote){
mRemote=remote;
}
publicandroid.os.IBinderasBinder(){
returnmRemote;
}
publicjava.lang.StringgetInterfaceDescriptor(){
returnDESCRIPTOR;
}
publicjava.lang.Stringgreet(java.lang.Stringsomeone)throwsandroid.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);
_data.writeString(someone);
mRemote.transact(Stub.TRANSACTION_greet,_data,_reply,0);
_reply.readException();
_result=_reply.readString();
}finally{
_reply.recycle();
_data.recycle();
}
return_result;
}
}
staticfinalintTRANSACTION_greet=(android.os.IBinder.FIRST_CALL_TRANSACTION+0);
}
publicjava.lang.Stringgreet(java.lang.Stringsomeone)throwsandroid.os.RemoteException;
}
該文件的大綱視圖如下:
IPerson接口中的抽象內部類Stub繼承android.os.Binder類并實現IPerson接口,比較重要的方法是asInterface(IBinder)方法,該方法會將IBinder類型的對象轉換成IPerson類型,必要的時候生成一個代理對象返回結果。
接下來就是我們的Service了:
[java]view plaincopy
packagecom.scott.server;
importandroid.app.Service;
importandroid.content.Intent;
importandroid.os.IBinder;
importandroid.os.RemoteException;
importandroid.util.Log;
importcom.scott.aidl.IPerson;
publicclassAIDLServiceextendsService{
privatestaticfinalStringTAG="AIDLService";
IPerson.Stubstub=newIPerson.Stub(){
@Override
publicStringgreet(Stringsomeone)throwsRemoteException{
Log.i(TAG,"greet()called");
return"hello,"+someone;
}
};
@Override
publicIBinderonBind(Intentintent){
Log.i(TAG,"onBind()called");
returnstub;
}
@Override
publicbooleanonUnbind(Intentintent){
Log.i(TAG,"onUnbind()called");
returntrue;
}
@Override
publicvoidonDestroy(){
super.onDestroy();
Log.i(TAG,"onDestroy()called");
}
}
我們實現了IPerson.Stub這個抽象類的greet方法,然后再onBind(Intent)方法中返回我們的stub實例,這樣一來調用方獲取的IPerson.Stub就是我們的這個實例,greet方法也會按照我們的期望那樣執行。
當然,要想讓Service生效,我們還需要在AndroidManifest.xml中做一些配置工作:
[java]view plaincopy
<serviceandroid:name=".AIDLService">
<intent-filter>
<actionandroid:name="android.intent.action.AIDLService"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
服務端已經完成了,接下來我們就該完成客戶端的工作了。我已經建好了一個客戶端工程,如圖:
我們只需要把IPerson.aidl文件拷到相應的目錄中即可,編譯器同樣會生成相對應的IPerson.java文件,這一部分和服務端沒什么區別。這樣一來,服務端和客戶端就在通信協議上達到了統一。我們主要工作在MainActivity中完成。
MainActivity代碼如下:
[java]view plaincopy
packagecom.scott.client;
importandroid.app.Activity;
importandroid.content.ComponentName;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.ServiceConnection;
importandroid.os.Bundle;
importandroid.os.IBinder;
importandroid.os.RemoteException;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.Toast;
importcom.scott.aidl.IPerson;
publicclassMainActivityextendsActivity{
privateButtonbindBtn;
privateButtongreetBtn;
privateButtonunbindBtn;
privateIPersonperson;
privateServiceConnectionconn=newServiceConnection(){
@Override
publicvoidonServiceConnected(ComponentNamename,IBinderservice){
Log.i("ServiceConnection","onServiceConnected()called");
person=IPerson.Stub.asInterface(service);
}
@Override
publicvoidonServiceDisconnected(ComponentNamename){
//Thisiscalledwhentheconnectionwiththeservicehasbeenunexpectedlydisconnected,
//thatis,itsprocesscrashed.Becauseitisrunninginoursameprocess,weshouldneverseethishappen.
Log.i("ServiceConnection","onServiceDisconnected()called");
}
};
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bindBtn=(Button)findViewById(R.id.bindBtn);
bindBtn.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
Intentintent=newIntent("android.intent.action.AIDLService");
bindService(intent,conn,Context.BIND_AUTO_CREATE);
bindBtn.setEnabled(false);
greetBtn.setEnabled(true);
unbindBtn.setEnabled(true);
}
});
greetBtn=(Button)findViewById(R.id.greetBtn);
greetBtn.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
try{
StringretVal=person.greet("scott");
Toast.makeText(MainActivity.this,retVal,Toast.LENGTH_SHORT).show();
}catch(RemoteExceptione){
Toast.makeText(MainActivity.this,"error",Toast.LENGTH_SHORT).show();
}
}
});
unbindBtn=(Button)findViewById(R.id.unbindBtn);
unbindBtn.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
unbindService(conn);
bindBtn.setEnabled(true);
greetBtn.setEnabled(false);
unbindBtn.setEnabled(false);
}
});
}
}
從代碼中可以看到,我們要重寫ServiceConnection中的onServiceConnected方法將IBinder類型的對像轉換成我們的IPerson類型。到現在我們就剩下最后一個步驟了,這個環節也是最為關鍵的,就是綁定我們需要的服務。我們通過服務端Service定義的“android.intent.action.AIDLService”這個標識符來綁定其服務,這樣客戶端和服務端就實現了通信的連接,我們就可以調用IPerson中的“問候”方法了。
最后,貼幾張客戶端演示過程圖。
按照順序分別是:初始界面;點擊bindService后界面;點擊greet后界面;點擊unbindService后界面。
操作過程中的日志如下:
總結
- 上一篇: vspythonqt混合_PYQT5 v
- 下一篇: HTML超链接