概述
Android系統中的各應用程序都運行在各自的進程中,進程之間通常是無法直接交換數據的。
Android提供了跨進程調用Service的功能,稱爲AIDL,AIDL全稱:android interface define language,
Android接口定義語言。
ADIL相當與兩個進程通信的協議,通過這個協議對進程間的通信進行了規範。按照該規範編寫
代碼即可實現進程間的通信。
AIDL接口文件
跨進程調用服務最關鍵一步是定義接口文件,該接口文件的擴展名是aidl,
1、在項目的src文件夾下定義一個AIDL接口文件。
2、AIDL接口的內部語法與Java很相似,後面通過例題來演示AIDL接口的語法。
3、.aidl接口文件創建成功後,Android系統會自動生成主文件名相同的.java接口文件。該文件
位於項目的gen文件夾下,該文件不能修改,是Android自動生成的。
操作步驟
以下需要創建兩個項目,一個項目是後臺服務,一個是啓動該後臺服務的客戶端。通過啓動實現兩個進程間的通信。
步驟1、創建Service項目,將該項目中的Activity類刪除,並從項目清單文件中將該Activity類的註冊代碼刪除。
步驟2~步驟4-創建AIDL接口文件:
創建一個包,在該包中創建一個Java類,該類名爲IMyService.java,該類代碼如下所示:
package com.tarena.exer11_04.aidl;
interface IMyService {
void play();
void pause();
}
步驟3、將IMyService.java改名爲IMyService.aidl,按如下方法操作:定位至以下路徑並將IMyService.java
的擴展名改爲.adil。
圖-6
提示:以上路徑是工作空間-workspace下的當前項目的src加包路徑
注意:擴展名必須是小寫。若資源管理器中不顯示擴展名請按以下方法將文件的擴展名顯示出來:
單擊資源管理器中的“工具欄”菜單,單擊其中的“文件夾選項”,顯示圖-7:
圖-7
步驟4、右擊項目名,在彈出的菜單中選擇Refresh項,刷新項目,如圖-8所示:
圖-8
圖-9是刷新後項目的部分結構示意圖:
圖-9
其中,藍框中的IMyService.java是Android根據紅框中的IMyService.aidl自動生成的接口。打開該接口文件,
圖-10是該接口的部分源代碼:
圖-10
說明
標註(1)-Stub類是IMyService接口的內部抽象類,該類繼承了Binder類,在步驟5中標註(1)所示:在定
義MyBinder類時,不是繼承了Binder而是繼承了IMyService.Stub類。
標註(2)是Stub類中的asInterface方法,該方法負責將service返回至client的對象轉換爲IMyService.Stub類型,
這點與進程內部的對象轉換不同。
標註(3)指向的兩個方法是步驟2定義的IMyService接口中聲明的兩個方法。這兩個方法將在步驟5中
標註(2)所指的代碼實現。
提示:
1. aidl文件中不能出現訪問限定符,如public。
2. aidl文件(包括所在包)在兩個項目中要完全一致。
步驟5、創建圖-9中的標註所指-com.tarena.exer11_04.aidlservice包,在該包下創建AIDLService.java類,
該類繼承Service類。該類代碼如下所示:
public class AIDLService extends Service {
private static final String tag="AIDLService";
private MyBinder mBinder;
@Override
public IBinder onBind(Intent intent) {
Log.i(tag,"service onBind()");
return mBinder;
}
//自定義內部類,注意該類繼承的是IMyService.Stub類
public class MyBinder extends IMyService.Stub{
//重寫play方法
@Override
public void play() throws RemoteException {
AIDLService.this.play();
}
//重寫pause方法
@Override
public void pause() throws RemoteException {
AIDLService.this.pause();
}
}
@Override
public void onCreate() {
super.onCreate();
mBinder=new MyBinder();
Log.i(tag,"service onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(tag,"serivce onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
//自定義方法
private void play(){
Log.i(tag,"service play()");
}
//自定義方法
private void pause(){
Log.i(tag,"service pause()");
}
}
說明:
1、 ALDLService類中定義了一個內部類MyBinder,該類繼承了IMyService.Stub類。這與進程
內的Service定義內部類不同。
2、 標註(3)所指的onCreate方法中創建了MyBinder的一個對象,並在onBind方法中返回該對象。
步驟6、在項目清單文件中註冊AIDLService類。
步驟7~步驟11創建客戶端項目,該項目用來啓動步驟1~步驟6創建的Service。
創建exer11_04-client項目
步驟8、在res/layout/main.xml中創建三個按鈕,該佈局文件的效果如圖-11所示:
圖-11
單擊圖-11中的bind按鈕將啓動並綁定exer11_04-service項目中的Service。
之後再單擊play按鈕,將執行exer11_04-service中的AIDLService.play()方法。
單擊pause按鈕將執行AIDLService.pause()方法,在日誌窗口中顯示圖-12所示的信息
圖-12
步驟9、將exer11_04-service項目中創建的IMyService.aidl接口連包一起復制過來,如圖-13所示:
圖-13
圖-13中藍框中的文件是Android自動在gen文件夾中生成的。
步驟10、打開圖-13中標註所指創建MainAct.java,類,該類中用於啓動並綁定,
還有調用服務中的play和pause方法的代碼如下所示:
@Override
public void onClick(View v) {
Intent intent=new Intent();
switch(v.getId()){
case R.id.btnBind://綁定按鈕
//以下啓動並綁定另一個進程中的service
intent.setAction(Constant.ACTION_AIDL);
bindService(intent, mConn, BIND_AUTO_CREATE);
break;
case R.id.btnPlay://play按鈕
if(mIsBind){
try {
mBinder.play();//調用service中的play方法
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case R.id.btnPause://pause按鈕
if(mIsBind){
try {
mBinder.pause();//調用service中的pause方法
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
步驟11、爲以上代碼中紅框內的mConn對象編寫創建該對象的代碼,如下所示:
public class MainAct extends Activity implements OnClickListener{
IMyService mBinder;//接口的一個引用
boolean mIsBind=false;//綁定值爲true,未綁定值爲false
private ServiceConnection mConn=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/*獲得另一個進程中的Service傳遞過來的對象-service,用
* IMyService.Stub.asInterface方法轉換該對象,這一點與進程內的通信不同
*/
mBinder=IMyService.Stub.asInterface(service);
mIsBind=true;//設置爲true,標誌綁定成功
Log.i("MainAcivity","onServiceConnected");
}
};
跨進程綁定服務與本地綁定服務的對比
跨進程調用並綁定服務與綁定本地(同一應用程序內部的服務稱爲本地服務)服務有所不同。
1、綁定本地服務是:本地的Service通過onBinder方法將裝載數據的IBinder對象傳遞給客戶端的ServiceConnection
對象的ServiceConnected方法的第二個參數service。並用Service中的內部類(自定義類)進行轉換,從而獲得從
服務中返回的對象,通過調用該對象
中的方法或屬性值達到與被綁定的服務交換數據和控制該服務的目的。
2、跨進程綁定服務,首先要定義一個擴展名是aidl的接口文件,該接口文件中聲明瞭被綁定服務所提供
的方法。這個接口文件要複製到客戶端程序中。
在服務中定義內部類時,不是直接繼承Binder類,而是繼承接口.Stub類(因Stub類已繼承了Binder)。
在客戶端的onServiceConnected方法中用IMyService.Stub.asInterface()方法轉換服務器端傳遞過來的對象。