在網上看到一篇介紹AIDL的文章,自己把代碼在2.2的sdk上跑了一下,稍微更改了一下原文中的代碼(下文中的代碼都是自己編譯通過後使用的代碼)並且添加了xml文件,大家可以按照這個代碼自己實現一遍。
以下部門來自網上其他朋友的文章(由於出處太多,無法找到原始的地址)
================================================================
在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.android.aidltest; import com.android.aidltest.ITaskCallback; interface ITaskBinder { boolean isTaskRunning(); void stopRunningTask(); void registerCallback(ITaskCallback cb); void unregisterCallback(ITaskCallback cb); }
其中: ITaskCallback在文件ITaskCallback.aidl中定義:
package com.android.aidltest; interface ITaskCallback { void actionPerformed(int actionId); }
注意: 理論上, 參數可以傳遞基本數據類型和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() { @Override public void unregisterCallback(ITaskCallback cb) throws RemoteException { printf("service on unregisterCallback"); // TODO Auto-generated method stub if(cb!=null) mCallbacks.unregister(cb); } @Override public void stopRunningTask() throws RemoteException { printf("service on stopRunningTask"); // TODO Auto-generated method stub } @Override public void registerCallback(ITaskCallback cb) throws RemoteException { printf("service on registerCallback"); // TODO Auto-generated method stub if(cb!=null) mCallbacks.register(cb); } @Override public boolean isTaskRunning() throws RemoteException { printf("service on isTaskRunning"); // TODO Auto-generated method stub return false; } };
在aidltest.java中ITaskCallback.aidl接口實現:
private ITaskCallback mCallback = new ITaskCallback.Stub() { @Override public void actionPerformed(int actionId) throws RemoteException { // TODO Auto-generated method stub printf("callback id = " + actionId); } };
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) { final int N = mCallbacks.beginBroadcast(); for (int i = 0; i < N; i++) { try { mCallbacks.getBroadcastItem(i).actionPerformed(val); }catch(RemoteException e) { } } mCallbacks.finishBroadcast(); }
AIDL的創建方法:
AIDL語法很簡單,可以用來聲明一個帶一個或多個方法的接口,也可以傳遞參數和返回值。 由於遠程調用的需要, 這些參數和返回值並不是任何類型.下面是些AIDL支持的數據類型:
1. 不需要import聲明的簡單Java編程語言類型(int,boolean等)
2. String, CharSequence不需要特殊聲明
3. List, Map和Parcelables類型, 這些類型內所包含的數據成員也只能是簡單數據類型, String等其他比支持的類型.
(另外: 我沒嘗試Parcelables, 在Eclipse+ADT下編譯不過, 或許以後會有所支持).
實現接口時有幾個原則:
.拋出的異常不要返回給調用者. 跨進程拋異常處理是不可取的.
.IPC調用是同步的。如果你知道一個IPC服務需要超過幾毫秒的時間才能完成地話,你應該避免在Activity的主線程中調用。 也就是IPC調用會掛起應用程序導致界面失去響應. 這種情況應該考慮單起一個線程來處理.
.不能在AIDL接口中聲明靜態屬性。
IPC的調用步驟:
1. 聲明一個接口類型的變量,該接口類型在.aidl文件中定義。
2. 實現ServiceConnection。
3. 調用ApplicationContext.bindService(),並在ServiceConnection實現中進行傳遞.
4. 在ServiceConnection.onServiceConnected()實現中,你會接收一個IBinder實例(被調用的Service). 調用
YourInterfaceName.Stub.asInterface((IBinder)service)將參數轉換爲YourInterface類型。
5. 調用接口中定義的方法。 你總要檢測到DeadObjectException異常,該異常在連接斷開時被拋出。它只會被遠程方法拋出。
6. 斷開連接,調用接口實例中的ApplicationContext.unbindService()
下面是整個程序:
1. ITaskCallback.aidl
package com.android.aidltest; interface ITaskCallback { void actionPerformed(int actionId); }
2. ITaskBinder.aidl
package com.android.aidltest; import com.android.aidltest.ITaskCallback; interface ITaskBinder { boolean isTaskRunning(); void stopRunningTask(); void registerCallback(ITaskCallback cb); void unregisterCallback(ITaskCallback cb); }
3. MyService.java
package com.android.aidltest; 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 { public void onCreate() { printf("service create"); } public void onStart(Intent intent, int startId) { printf("service start id = " + startId); callback(startId); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub printf("service on bind"); return mBinder; } public void onDestroy() { printf("service on destroy"); super.onDestroy(); } 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) { } } mCallbacks.finishBroadcast(); } private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() { @Override public void unregisterCallback(ITaskCallback cb) throws RemoteException { printf("service on unregisterCallback"); // TODO Auto-generated method stub if(cb!=null) mCallbacks.unregister(cb); } @Override public void stopRunningTask() throws RemoteException { printf("service on stopRunningTask"); // TODO Auto-generated method stub } @Override public void registerCallback(ITaskCallback cb) throws RemoteException { printf("service on registerCallback"); // TODO Auto-generated method stub if(cb!=null) mCallbacks.register(cb); } @Override public boolean isTaskRunning() throws RemoteException { printf("service on isTaskRunning"); // TODO Auto-generated method stub return false; } }; final RemoteCallbackList<ITaskCallback> mCallbacks = new RemoteCallbackList<ITaskCallback>(); }
4. aidltest.java
package com.android.aidltest; 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; public class AidlTest extends Activity { private Button btnOk; private Button btnCancel; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnOk = (Button)findViewById(R.id.btn_ok); btnCancel = (Button)findViewById(R.id.btn_cancel); btnOk.setText("Start Service"); btnCancel.setText("Stop Service"); btnOk.setOnClickListener(new OnClickListener() { public void onClick(View v) { onOkClick(); } }); btnCancel.setOnClickListener(new OnClickListener() { public void onClick(View v) { onCancelClick(); } }); } void onOkClick() { printf("clicked ok"); Bundle args = new Bundle(); Intent intent = new Intent(this, MyService.class); intent.putExtras(args); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); startService(intent); } void onCancelClick() { printf("clicked cancel"); Intent intent = new Intent(this, MyService.class); unbindService(mConnection); } 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() { @Override public void actionPerformed(int actionId) throws RemoteException { // TODO Auto-generated method stub printf("callback id = " + actionId); } }; }
5. xml文件
AndroidMenifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.aidltest" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".AidlTest" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MyService" > <intent-filter> <action android:name="com.android.aidltest.START_MYSERVICE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> <uses-sdk android:minSdkVersion="8" /> </manifest>
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/btn_ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_ok" /> <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_cancel" /> </LinearLayout>
string.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, AidlTest!</string> <string name="app_name">AIDL Test</string> <string name="btn_ok">OK</string> <string name="btn_cancel">Cancel</string> </resources>