在Android中, 每個應用程式都可以有自己的進程. 在寫UI應用的時候, 經常要用到Service. 在不同的進程中, 怎樣傳遞對象呢? 顯然, Java中不允許跨進程記憶體共用. 因此傳遞對象, 只能把對象拆分成作業系統能理解的簡單形式, 以達到跨界對象訪問的目的. 在J2EE中,採用RMI的方式, 可以通過序列化傳遞對象. 在Android中, 則採用AIDL的方式. 理論上AIDL可以傳遞Bundle,實際上做起來卻比較麻煩.
AIDL(AndRoid介面描述語言)是一種借口描述語言; 編譯器可以通過aidl檔案產生一段代碼,通過預先定義的介面達到兩個進程內部通訊進程的目的. 如果需要在一個Activity中, 訪問另一個Service中的某個對象, 需要先將對象轉化成AIDL可識別的參數(可能是多個參數), 然後使用AIDL來傳遞這些參數, 在訊息的接收端, 使用這些參數組裝成自己需要的對象.
AIDL的IPC的機制和COM或CORBA類似, 是基於介面的,但它是輕量級的。它使用代理類在用戶端和實現層間傳遞值. 如果要使用AIDL, 需要完成2件事情:
1. 引入AIDL的相關類.;
2. 調用aidl產生的class.
具體實現步驟如下:
1、建立AIDL檔案, 在這個檔案裡面定義介面, 該介面定義了可供用戶端訪問的方法和屬性。 如: ITaskBinder.adil package com.cmcc.demo;<br />import com.cmcc.demo.ITaskCallback;<br />interface ITaskBinder<br />{<br />boolean isTaskRunning();<br />void stopRunningTask();<br />void registerCallback(ITaskCallback cb);<br />void unregisterCallback(ITaskCallback cb);<br />}
其中: ITaskCallback在檔案ITaskCallback.aidl中定義:
package com.cmcc.demo;<br />interface ITaskCallback<br />{<br />void actionPerformed(int actionId);<br />}
注意: 理論上, 參數可以傳遞基礎資料型別 (Elementary Data Type)和String, 還有就是Bundle的衍生類別, 不過在Eclipse中,目前的ADT不支援Bundle做為參數, 據說用Ant編譯可以, 我沒做嘗試.
2、編譯AIDL檔案, 用Ant的話, 可能需要手動, 使用Eclipse plugin的話,可以根據adil檔案自動生產java檔案並編譯, 不需要人為介入.
3、在Java檔案中, 實現AIDL中定義的介面. 編譯器會根據AIDL介面, 產生一個JAVA介面。這個介面有一個名為Stub的內部抽象類別,它繼承擴充了介面並實現了遠程調用需要的幾個方法。接下來就需要自己去實現自訂的幾個介面了.
ITaskBinder.aidl中介面的實現, 在MyService.java中介面以內嵌類的方式實現:
private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {<br />public void stopRunningTask() { //@TODO }<br />public boolean isTaskRunning() { //@TODO return false; }<br />public void registerCallback(ITaskCallback cb)<br />{<br />if (cb != null) mCallbacks.register(cb);<br />}<br /> public void unregisterCallback(ITaskCallback cb)<br />{<br />if (cb != null) mCallbacks.unregister(cb);<br /> }<br />};<br />在MyActivity.java中ITaskCallback.aidl介面實現:<br />private ITaskCallback mCallback = new ITaskCallback.Stub() {<br />public void actionPerformed(int id) { //TODO printf("callback id=" + id);<br /> }<br />};
4、向用戶端提供介面ITaskBinder, 如果寫的是service,擴充該Service並重載onBind ()方法來返回一個實現上述介面的類的執行個體。這個地方返回的mBinder,就是上面通過內嵌了定義的那個. (MyService.java)
public IBinder onBind(Intent t) { printf("service on bind"); return mBinder; }
在Activity中, 可以通過Binder定義的介面, 來進行遠程調用.
5、在伺服器端回調用戶端的函數. 前提是當用戶端擷取的IBinder介面的時候,要去註冊回呼函數, 只有這樣, 伺服器端才知道該調用那些函數在:MyService.java中:
void callback(int val) {<br />final int N = mCallbacks.beginBroadcast();<br />for (int i=0; i<N; i++) {<br />try {<br />mCallbacks.getBroadcastItem(i).actionPerformed(val);<br />} catch (RemoteException e) {<br />// The RemoteCallbackList will take care of removing<br />// the dead object for us.<br />}<br />}<br />mCallbacks.finishBroadcast();<br />}