http://blog.chinaunix.net/u3/90876/showart_2200991.html
在Android中, 每個應用程序都可以有自己的進程. 在寫UI應用的時候, 經(jīng)常要用到Service. 在不同的進程中, 怎樣傳遞對象呢? 顯然, Java中不允許跨進程內(nèi)存共享. 因此傳遞對象, 只能把對象拆分成操作系統(tǒng)能理解的簡單形式, 以達到跨界對象訪問的目的. 在J2EE中,采用RMI的方式, 可以通過序列化傳遞對象. 在Android中, 則采用AIDL的方式. 理論上AIDL可以傳遞Bundle,實際上做起來卻比較麻煩.
AIDL(AndRoid接口描述語言)是一種借口描述語言; 編譯器可以通過aidl文件生成一段代碼,通過預先定義的接口達到兩個進程內(nèi)部通信進程的目的. 如果需要在一個Activity中, 訪問另一個Service中的某個對象, 需要先將對象轉(zhuǎn)化成AIDL可識別的參數(shù)(可能是多個參數(shù)), 然后使用AIDL來傳遞這些參數(shù), 在消息的接收端, 使用這些參數(shù)組裝成自己需要的對象.
AIDL的IPC的機制和COM或CORBA類似, 是基于接口的,但它是輕量級的。它使用代理類在客戶端和實現(xiàn)層間傳遞值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相關(guān)類.; 2. 調(diào)用aidl產(chǎn)生的class.
具體實現(xiàn)步驟如下:
1、創(chuàng)建AIDL文件, 在這個文件里面定義接口, 該接口定義了可供客戶端訪問的方法和屬性。 如: 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的話, 可能需要手動, 使用Eclipse plugin的話,可以根據(jù)adil文件自動生產(chǎn)java文件并編譯, 不需要人為介入.
3、在Java文件中, 實現(xiàn)AIDL中定義的接口. 編譯器會根據(jù)AIDL接口, 產(chǎn)生一個JAVA接口。這個接口有一個名為Stub的內(nèi)部抽象類,它繼承擴展了接口并實現(xiàn)了遠程調(diào)用需要的幾個方法。接下來就需要自己去實現(xiàn)自定義的幾個接口了.
ITaskBinder.aidl中接口的實現(xiàn), 在MyService.java中接口以內(nèi)嵌類的方式實現(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接口實現(xiàn):
private ITaskCallback mCallback = new ITaskCallback.Stub() {
public void actionPerformed(int id) { //TODO printf("callback id=" + id); } };
|
4、向客戶端提供接口ITaskBinder, 如果寫的是service,擴展該Service并重載onBind ()方法來返回一個實現(xiàn)上述接口的類的實例。這個地方返回的mBinder,就是上面通過內(nèi)嵌了定義的那個. (MyService.java)
public IBinder onBind(Intent t) { printf("service on bind"); return mBinder; }
在Activity中, 可以通過Binder定義的接口, 來進行遠程調(diào)用.
5、在服務器端回調(diào)客戶端的函數(shù). 前提是當客戶端獲取的IBinder接口的時候,要去注冊回調(diào)函數(shù), 只有這樣, 服務器端才知道該調(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語法很簡單,可以用來聲明一個帶一個或多個方法的接口,也可以傳遞參數(shù)和返回值。由于遠程調(diào)用的需要, 這些參數(shù)和返回值并不是任何類型.下面是些AIDL支持的數(shù)據(jù)類型:
1. 不需要import聲明的簡單Java編程語言類型(int,boolean等)
2. String, CharSequence不需要特殊聲明
3. List, Map和Parcelables類型, 這些類型內(nèi)所包含的數(shù)據(jù)成員也只能是簡單數(shù)據(jù)類型, String等其他比支持的類型.
(
(另外: 我沒嘗試Parcelables, 在Eclipse+ADT下編譯不過, 或許以后會有所支持).
下面是AIDL語法:
// 文件名: SomeClass.aidl // 文件可以有注釋, 跟java的一樣 // 在package以前的注釋, 將會被忽略. // 函數(shù)和變量以前的注釋, 都會被加入到生產(chǎn)java代碼中. package com.cmcc.demo;
// import 引入語句 import com.cmcc.demo.ITaskCallback;
interface ITaskBinder {
//函數(shù)跟java一樣, 可以有0到多個參數(shù) ,可以有一個返回值 boolean isTaskRunning();
void stopRunningTask(); //參數(shù)可以是另外的一個aidl定義的接口 void registerCallback(ITaskCallback cb);
void unregisterCallback(ITaskCallback cb);
//參數(shù)可以是String, 可以用in表入輸入類型, out表示輸出類型.
int getCustomerList(in String branch, out String customerList);
}
實現(xiàn)接口時有幾個原則:
.拋出的異常不要返回給調(diào)用者. 跨進程拋異常處理是不可取的.
.IPC調(diào)用是同步的。如果你知道一個IPC服務需要超過幾毫秒的時間才能完成地話,你應該避免在Activity的主線程中調(diào)用。 也就是IPC調(diào)用會掛起應用程序?qū)е陆缑媸ロ憫? 這種情況應該考慮單起一個線程來處理.
.不能在AIDL接口中聲明靜態(tài)屬性。
IPC的調(diào)用步驟:
1. 聲明一個接口類型的變量,該接口類型在.aidl文件中定義。
2. 實現(xiàn)ServiceConnection。
3. 調(diào)用ApplicationContext.bindService(),并在ServiceConnection實現(xiàn)中進行傳遞.
4. 在ServiceConnection.onServiceConnected()實現(xiàn)中,你會接收一個IBinder實例(被調(diào)用的Service). 調(diào)用 YourInterfaceName.Stub.asInterface((IBinder)service)將參數(shù)轉(zhuǎn)換為YourInterface類型。
5. 調(diào)用接口中定義的方法。 你總要檢測到DeadObjectException異常,該異常在連接斷開時被拋出。它只會被遠程方法拋出。
6. 斷開連接,調(diào)用接口實例中的ApplicationContext.unbindService()
整個源碼程序如下所示:
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); } }; }
|
5. xml文件(略)
|