Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AIDL IPC機制詳解

Android AIDL IPC機制詳解

編輯:關於Android編程

Introduction

由於每個應用程序都運行在自己的進程空間,並且可以從應用程序UI運行另一個服務進程,而且經常會在不同的進程間傳遞對象。在Android平台,一個進程通常不能訪問另一個進程的內存空間,所以要想對話,需要將對象分解成操作系統可以理解的基本單元,並且有序的通過進程邊界。通過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。

AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。

AIDL適用場景

官方文檔特別提醒我們何時使用AIDL是必要的:只有你允許客戶端從不同的應用程序為了進程間的通信而去訪問你的service,以及想在你的service處理多線程。具體的不同場景下的AIDL設計規則如下:

如果不需要進行不同應用程序間的並發通信(IPC),通過實現一個Binder對象來創建接口;或者你想進行IPC,但不需要處理多線程的,則使用Messenger對象來創建接口。無論如何,在使用AIDL前,必須要理解如何綁定service——bindService。

AIDL開發步驟

AIDL接口文件,和普通的接口內容沒有什麼特別,只是它的擴展名為.aidl,保存在src目錄下。如果其他應用程序需要IPC,則那些應用程序的src也要帶有這個文件。Android SDK tools就會在gen目錄自動生成一個IBinder接口文件。service必須適當地實現這個IBinder接口。那麼客戶端程序就能綁定這個service並在IPC時從IBinder調用方法。每個aidl文件只能定義一個接口,而且只能是接口的聲明和方法的聲明。

換言之,AIDL文件的作用即是在

1 創建.aidl文件

任何需要跨進程調用的方法都需要聲明在涉及的每一個服務或者進程中。
AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。其中對於Java編程語言的基本數據類型 (int, long, char, boolean等),String和CharSequence,集合接口類型List和Map,不需要import 語句。而如果需要在AIDL中使用其他AIDL接口類型,需要import,即使是在相同包結構下。AIDL允許傳遞實現Parcelable接口的類,需要import。 需要特別注意的是,對於非基本數據類型,也不是String和CharSequence類型的,需要有方向指示,包括in、out和inout,in表示由客戶端設置,out表示由服務端設置,inout是兩者均可設置。AIDL只支持接口方法,不能公開static變量。

例如

l AIDLService .aidl: 這個AIDL用於聲明需要在客戶端中調用的服務中定義的方法。

注意,在某個aidl文件中可以調用其他aidl文件。

package com.kevin.android.demos.binder.aidl;  
import com.kevin.android.demos.binder.aidl.AIDLActivity;
interface AIDLService {   
    void registerTestCall(AIDLActivity cb);   
    void invokCallBack();
}  


l AIDLActivity.aidl:這個aidl主要聲明在服務中所需要調用的客戶端的方法。

package com.kevin.android.demos.binder.aidl;

import com.kevin.android.demos.binder.aidl.Rect1;

interface AIDLActivity {

void performAction(in Rect1 rect);

}

l Rect1.aidl:這個aidl主要聲明服務與客戶端之間公用的數據結構。

package com.kevin.android.demos.binder.aidl;

parcelable Rect1;

2 實現服務端接口

創建一個類實現剛才那個aidl的接口:

/*****************Copyright (C), 2010-2015, FORYOU Tech. Co., Ltd.********************/
package com.kevin.android.demos.binder;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.kevin.android.demos.binder.aidl.AIDLActivity;
import com.kevin.android.demos.binder.aidl.AIDLService;
import com.kevin.android.demos.binder.aidl.Rect1;
public class AIDLTestService extends Service {
private AIDLActivity callback;
private void Log(String str) {
Log.d(Constant.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 AIDLService.Stub mBinder = new AIDLService.Stub() {
@Override
public void invokCallBack() throws RemoteException {
Log("AIDLService.invokCallBack");
Rect1 rect = new Rect1();
rect.bottom=-1;
rect.left=-1;
rect.right=1;
rect.top=1;
callback.performAction(rect);
}
@Override
public void registerTestCall(AIDLActivity cb) throws RemoteException {
Log("AIDLService.registerTestCall");
callback = cb;
}
};
}


這裡會看到有一個名為AIDLService.Stub類,查看aidl文件生成的Java文件源代碼就能發現有這麼一段代碼:

/** Local-side IPC implementation stub class. */

public static abstract class Stub extends android.os.Binder implements com.kevin.android.demos.binder.aidl.AIDLService

原來Stub類就是繼承於Binder類,也就是說RemoteService類和普通的Service類沒什麼不同,只是所返回的IBinder對象比較特別,是一個實現了AIDL接口的Binder。

數據Bean實現

接下來就是關於所傳遞的數據Bean——Rect1類,是一個序列化的類,這裡使用Parcelable 接口來序列化,是Android提供的一個比Serializable 效率更高的序列化類。

Parcelable需要實現三個函數:
1) void writeToParcel(Parcel dest, int flags) 將需要序列化存儲的數據寫入外部提供的Parcel對象dest。讀取Parcel數據的次序要和這裡的write次序一致,否則可能會讀錯數據。
2) describeContents() :簡單地返回0即可。
3) static final Parcelable.Creator對象CREATOR 這個CREATOR命名是固定的,而它對應的接口有兩個方法:

l createFromParcel(Parcel source) 實現從source創建出JavaBean實例的功能。

l newArray(int size) 創建一個類型為T,長度為size的數組,僅一句話(return new T[size])即可。估計本方法是供外部類反序列化本類數組使用。

/*****************Copyright (C), 2010-2015, FORYOU Tech. Co., Ltd.********************/
package com.kevin.android.demos.binder.aidl;
import android.os.Parcel;
import android.os.Parcelable;
 
public class Rect1 implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Rect1 createFromParcel(Parcel in) {
return new Rect1(in);
}
public Rect1[] newArray(int size) {
return new Rect1[size];
}
};
public Rect1() {
}
private Rect1(Parcel in) {
readFromParcel(in);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
}


對於實現AIDL接口,官方還提醒我們:

1. 調用者是不能保證在主線程執行的,所以從一調用的開始就需要考慮多線程處理,以及確保線程安全;

2. IPC調用是同步的。如果你知道一個IPC服務需要超過幾毫秒的時間才能完成地話,你應該避免在Activity的主線程中調用。也就是IPC調用會掛起應用程序導致界面失去響應,這種情況應該考慮單獨開啟一個線程來處理。
3. 拋出的異常是不能返回給調用者(跨進程拋異常處理是不可取的)。

3 客戶端獲取接口

客戶端如何獲取AIDL接口呢?通過AIDLService .Stub.asInterface(service)來得到IMyService對象:

/*****************Copyright (C), 2010-2015, FORYOU Tech. Co., Ltd.********************/
package com.kevin.android.demos.binder;
import java.text.MessageFormat;
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;
import com.kevin.android.demos.binder.aidl.AIDLActivity;
import com.kevin.android.demos.binder.aidl.AIDLService;
import com.kevin.android.demos.binder.aidl.Rect1;
public class AIDLTestActivity extends Activity {
private Button btnOk;
private Button btnCancel;
private Button btnCallBack;
private void Log(String str) {
Log.d(Constant.TAG, "------ " + str + "------");
}
 
/*Activity中也要實現一個AIDL接口,本質上也為一個bind類*/
private AIDLActivity.Stub mCallback = new AIDLActivity.Stub() {
@Override
public void performAction(Rect1 rect) throws RemoteException {
Log("AIDLActivity.performAction");
String str = MessageFormat.format(
"rect[bottom={0},top={1},left={2},right={3}]", rect.bottom,
rect.top, rect.left, rect.right);
Toast.makeText(AIDLTestActivity.this,
"this toast is called from service\n" + str, 1).show();
}
};
/*以下是獲取到遠端的Service連接的方法,聲明一個匿名類*/
AIDLService mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log("connect service");
mService = AIDLService.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) {
Log("AIDLTestActivity.onCreate");
super.onCreate(icicle);
setContentView(R.layout.aidl_activity);
btnOk = (Button) findViewById(R.id.btn_ok);
btnCancel = (Button) findViewById(R.id.btn_cancel);
btnCallBack = (Button) findViewById(R.id.btn_call_back);
btnOk.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Log("AIDLTestActivity.btnOk");
Bundle args = new Bundle();
Intent intent = new Intent();
intent.setAction("com.kevin.android.demos.binder.AIDLTestService");
intent.putExtras(args);
/*Activity中綁定服務的方式還是類似傳統中的*/
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
startService(intent);
}
});
btnCancel.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Log("AIDLTestActivity.btnCancel");
unbindService(mConnection);
// stopService(intent);
}
});
btnCallBack.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log("AIDLTestActivity.btnCallBack");
try {
mService.invokCallBack();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}


4 效果

\

Permission權限

如果Service在AndroidManifest.xml中聲明了全局的強制的訪問權限,其他引用必須聲明權限才能來start,stop或bind這個service.
另外,service可以通過權限來保護她的IPC方法調用,通過調用checkCallingPermission(String)方法來確保可以執行這個操作。

AndroidManifest.xml的Service元素





android:process=":remote",代表在應用程序裡,當需要該service時,會自動創建新的進程。而如果是android:process="remote",沒有“:”分號的,則創建全局進程,不同的應用程序共享該進程。

常見問題:

無法由aidl文件生成java類文件

將java se的等級由1.5改為1.6

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved