AIDL的IPC的機(jī)制和COM或CORBA類似, 是基于接口的,但它是輕量級(jí)的。它使用代理類在客戶端和實(shí)現(xiàn)層間傳遞值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相關(guān)類.; 2.
調(diào)用aidl產(chǎn)生的class.
具體實(shí)現(xiàn)步驟如下:
1、創(chuàng)建AIDL文件,
在這個(gè)文件里面定義接口, 該接口定義了可供客戶端訪問的方法和屬性。 如: ITaskBinder.adil
package com.cmcc.demo;
import com.cmcc.demo.ITaskCallback;
interface ITaskBinder {
boolean isTaskRunning();
void stopRunningTask();
void registerCallback(ITaskCallback cb);
void unregisterCallback(ITaskCallback cb);
}
其中: ITaskCallback在文件ITaskCallback.aidl中定義:
package com.cmcc.demo;
interface ITaskCallback {
void
actionPerformed(int actionId);
}
注意: 理論上, 參數(shù)可以傳遞基本數(shù)據(jù)類型和String, 還有就是Bundle的派生類, 不過在Eclipse中,目前的ADT不支持Bundle做為參數(shù), 據(jù)說用Ant編譯可以, 我沒做嘗試.
2、編譯AIDL文件,
用Ant的話, 可能需要手動(dòng), 使用Eclipse plugin的話,可以根據(jù)adil文件自動(dòng)生產(chǎn)java文件并編譯,
不需要人為介入.
3、在Java文件中,
實(shí)現(xiàn)AIDL中定義的接口. 編譯器會(huì)根據(jù)AIDL接口, 產(chǎn)生一個(gè)JAVA接口。這個(gè)接口有一個(gè)名為Stub的內(nèi)部抽象類,它繼承擴(kuò)展了接口并實(shí)現(xiàn)了遠(yuǎn)程調(diào)用需要的幾個(gè)方法。接下來就需要自己去實(shí)現(xiàn)自定義的幾個(gè)接口了.
ITaskBinder.aidl中接口的實(shí)現(xiàn), 在MyService.java中接口以內(nèi)嵌類的方式實(shí)現(xiàn):
private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {
public void stopRunningTask() {
//@TODO
}
public boolean isTaskRunning() {
//@TODO
return false;
}
public void registerCallback(ITaskCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(ITaskCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
};
在MyActivity.java中ITaskCallback.aidl接口實(shí)現(xiàn):
private ITaskCallback mCallback = new ITaskCallback.Stub() {
public void actionPerformed(int id) {
//TODO
printf("callback id=" + id);
}
};
4、向客戶端提供接口ITaskBinder, 如果寫的是service,擴(kuò)展該Service并重載onBind ()方法來返回一個(gè)實(shí)現(xiàn)上述接口的類的實(shí)例。這個(gè)地方返回的mBinder,就是上面通過內(nèi)嵌了定義的那個(gè). (MyService.java)
public IBinder onBind(Intent t) {
printf("service on bind");
return mBinder;
}
在Activity中, 可以通過Binder定義的接口, 來進(jìn)行遠(yuǎn)程調(diào)用.
5、在服務(wù)器端回調(diào)客戶端的函數(shù). 前提是當(dāng)客戶端獲取的IBinder接口的時(shí)候,要去注冊(cè)回調(diào)函數(shù), 只有這樣, 服務(wù)器端才知道該調(diào)用那些函數(shù)在:MyService.java中:
void callback(int val) {
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).actionPerformed(val);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
}
AIDL的創(chuàng)建方法:
AIDL語法很簡(jiǎn)單,可以用來聲明一個(gè)帶一個(gè)或多個(gè)方法的接口,也可以傳遞參數(shù)和返回值。
由于遠(yuǎn)程調(diào)用的需要, 這些參數(shù)和返回值并不是任何類型.下面是些AIDL支持的數(shù)據(jù)類型:
1. 不需要import聲明的簡(jiǎn)單Java編程語言類型(int,boolean等)
2. String, CharSequence不需要特殊聲明
3. List, Map和Parcelables類型, 這些類型內(nèi)所包含的數(shù)據(jù)成員也只能是簡(jiǎn)單數(shù)據(jù)類型, String等其他比支持的類型.
(
(另外: 我沒嘗試Parcelables, 在Eclipse+ADT下編譯不過, 或許以后會(huì)有所支持).
下面是AIDL語法:
// 文件名: SomeClass.aidl
// 文件可以有注釋, 跟java的一樣
// 在package以前的注釋, 將會(huì)被忽略.
// 函數(shù)和變量以前的注釋, 都會(huì)被加入到生產(chǎn)java代碼中.
package com.cmcc.demo;
// import 引入語句
import com.cmcc.demo.ITaskCallback;
interface ITaskBinder {
//函數(shù)跟java一樣, 可以有0到多個(gè)參數(shù) ,可以有一個(gè)返回值
boolean isTaskRunning();
void stopRunningTask();
//參數(shù)可以是另外的一個(gè)aidl定義的接口
void registerCallback(ITaskCallback cb);
void
unregisterCallback(ITaskCallback cb);
//參數(shù)可以是String, 可以用in表入輸入類型, out表示輸出類型.
int getCustomerList(in String branch, out String[] customerList);
}
實(shí)現(xiàn)接口時(shí)有幾個(gè)原則:
.拋出的異常不要返回給調(diào)用者. 跨進(jìn)程拋異常處理是不可取的.
.IPC調(diào)用是同步的。如果你知道一個(gè)IPC服務(wù)需要超過幾毫秒的時(shí)間才能完成地話,你應(yīng)該避免在Activity的主線程中調(diào)用。 也就是IPC調(diào)用會(huì)掛起應(yīng)用程序?qū)е陆缑媸ロ憫?yīng). 這種情況應(yīng)該考慮單起一個(gè)線程來處理.
.不能在AIDL接口中聲明靜態(tài)屬性。
IPC的調(diào)用步驟:
1. 聲明一個(gè)接口類型的變量,該接口類型在.aidl文件中定義。
2. 實(shí)現(xiàn)ServiceConnection。
3. 調(diào)用ApplicationContext.bindService(),并在ServiceConnection實(shí)現(xiàn)中進(jìn)行傳遞.
4. 在ServiceConnection.onServiceConnected()實(shí)現(xiàn)中,你會(huì)接收一個(gè)IBinder實(shí)例(被調(diào)用的Service). 調(diào)用
YourInterfaceName.Stub.asInterface((IBinder)service)將參數(shù)轉(zhuǎn)換為YourInterface類型。
5. 調(diào)用接口中定義的方法。 你總要檢測(cè)到DeadObjectException異常,該異常在連接斷開時(shí)被拋出。它只會(huì)被遠(yuǎn)程方法拋出。
6. 斷開連接,調(diào)用接口實(shí)例中的ApplicationContext.unbindService()
下面是整個(gè)程序:
1. ITaskCallback.aidl
package com.cmcc.demo;
interface ITaskCallback {
void
actionPerformed(int actionId);
}
2.
ITaskBinder.aidl
package com.cmcc.demo;
import com.cmcc.demo.ITaskCallback;
interface ITaskBinder {
boolean isTaskRunning();
void stopRunningTask();
void registerCallback(ITaskCallback cb);
void unregisterCallback(ITaskCallback cb);
}
3. MyService.java
package com.cmcc.demo;
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 MyService extends Service {
@Override
public void onCreate() {
printf("service create");
}
@Override
public void onStart(Intent intent, int startId) {
printf("service start id=" + startId);
callback(startId);
}
@Override
public IBinder onBind(Intent t) {
printf("service on bind");
return mBinder;
}
@Override
public void onDestroy() {
printf("service on destroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
printf("service on unbind");
return super.onUnbind(intent);
}
public void onRebind(Intent intent) {
printf("service on rebind");
super.onRebind(intent);
}
private void printf(String str) {
Log.e("TAG", "###################------ " + str + "------");
}
void callback(int val) {
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).actionPerformed(val);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
}
private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {
public void stopRunningTask() {
}
public boolean isTaskRunning() {
return false;
}
public void registerCallback(ITaskCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(ITaskCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
};
final RemoteCallbackList<ITaskCallback> mCallbacks
= new RemoteCallbackList<ITaskCallback>();
}
4.
MyActivity.java
package com.cmcc.demo;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AbsoluteLayout;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
public class MyActivity
extends Activity {
private Button btnOk;
private Button btnCancel;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.test_service);
btnOk = (Button)findViewById(R.id.btn_ok);
btnCancel = (Button)findViewById(R.id.btn_cancel);
btnOk.setText("Start Service");
btnCancel.setTag("Stop Service");
btnOk.setOnClickListener(new OnClickListener()
{
public void onClick(View v) {
onOkClick();
}
});
btnCancel.setOnClickListener(new OnClickListener()
{
public void onClick(View v) {
onCancelClick();
}
});
}
void onOkClick() {
Bundle args = new Bundle();
Intent intent = new Intent(this, MyService.class);
intent.putExtras(args);
//printf("send intent to start");
//startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
startService(intent);
}
void onCancelClick() {
Intent intent = new Intent(this, MyService.class);
//printf("send intent to stop");
unbindService(mConnection);
//stopService(intent);
}
private void printf(String str) {
Log.e("TAG", "###################------ " + str + "------");
}
ITaskBinder mService;
private ServiceConnection mConnection = new
ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
mService = ITaskBinder.Stub.asInterface(service);
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName
className) {
mService = null;
}
};
private ITaskCallback mCallback = new
ITaskCallback.Stub() {
public void actionPerformed(int id) {
printf("callback id=" + id);
}
};
}