爲使應用程序之間可以彼此通訊,Android提供了IPC (Inter Process Communication,進程間通訊)的一種獨特實現: AIDL (Android Interface Definition Language, Android接口定義語言)。java
創建兩個Android項目,一個是client,一個是server(提供service)。android
這篇文章將經過一個項目來介紹AIDL用法,包含了service和client。可能簡單了些,不太輕省許多。app
本文提供了一個關於AIDL使用的簡單易懂的例子,分爲客戶端和服務端兩部分,分別爲客戶端和服務端新建一個eclipse工程,實現了從客戶端向服務端發送請求,服務端打印log的功能。eclipse
客戶端和服務端的源碼結構以下:ide
注意,因爲客戶端和服務端的aidl文件所在包名必須同樣,而兩個包名同樣的程序在安裝時會產生衝突,因此這裏用了一個技巧,在客戶端工程的AndroidManifest.xml裏把包名指定爲com.styleflying,因此你們就會看到gen目錄下的R.java所在的包是com.styleflying而不是com.styleflying.AIDL函數
如今客戶端和服務端工程分別新建一個aidl接口,所在包和文件名必須同樣。兩個aidl接口是同樣的,內容以下:oop
package com.styleflying.AIDL; interface mInterface{ void invokTest(); }
自動編譯生成.java文件以下:this
package com.styleflying.AIDL; public interface mInterface extends IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends Binder implements mInterface { private static final String DESCRIPTOR = "com.styleflying.AIDL.mInterface"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.styleflying.AIDL.mInterface interface, * generating a proxy if needed. */ public static mInterface asInterface(IBinder obj) { if ((obj==null)) { return null; } IInterface iin = (IInterface)obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof mInterface))) { return ((com.styleflying.AIDL.mInterface)iin); } return new com.styleflying.AIDL.mInterface.Stub.Proxy(obj); } public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case INTERFACE_TRANSACTION: reply.writeString(DESCRIPTOR); return true; case TRANSACTION_invokTest: data.enforceInterface(DESCRIPTOR); this.invokTest(); reply.writeNoException(); return true; } return super.onTransact(code, data, reply, flags); } private static class Proxy implements mInterface { private IBinder mRemote; Proxy(IBinder remote) { mRemote = remote; } public IBinder asBinder() { return mRemote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } public void invokTest() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_invokTest, _data, _reply, 0); _reply.readException(); }finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_invokTest = (IBinder.FIRST_CALL_TRANSACTION + 0); } public void invokTest() throws RemoteException; }
客戶端的mAIDLActivity.java以下:spa
package com.styleflying.AIDL; 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.styleflying.R; public class mAIDLActivity extends Activity { private static final String TAG = "AIDLActivity"; private Button btnOk; private Button btnCancel; private Button btnCallBack; private void Log(String str){ Log.d(TAG,"----------" + str + "----------"); } mInterface mService; private ServiceConnection mConnection = new ServiceConnection(){ public void onServiceConnected(ComponentName className, IBinder service){ Log("connect service"); mService = mInterface.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName className){ Log("disconnect service"); mService = null; } }; /** 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); btnCallBack = (Button)findViewById(R.id.btn_callback); btnOk.setOnClickListener(new OnClickListener(){ public void onClick(View v){ Bundle args = new Bundle(); Intent intent = new Intent("com.styleflying.AIDL.service"); intent.putExtras(args); bindService(intent,mConnection,Context.BIND_AUTO_CREATE); } }); btnCancel.setOnClickListener(new OnClickListener(){ public void onClick(View v){ unbindService(mConnection); } }); btnCallBack.setOnClickListener(new OnClickListener(){ public void onClick(View v){ try{ Log.i(TAG,"current Thread id = " + Thread.currentThread().getId()); mService.invokTest(); } catch(RemoteException e){ } } }); } }
客戶端在執行bindService的時候,成功綁定服務以後,會回調mConnection的onServiceConnected(),而且傳回了服務端的通訊接口IBinder,此IBinder即服務onBind()時返回的IBinder,詳見mAIDLService.java。代理
在onServiceConnected(),客戶端成功獲取了服務端通訊接口,其實是本地代理對象,該對象存在於客戶端進程空間,客戶端只和代理對象交互,真正的IPC通訊是本地代理對象和服務端的通訊。
mAIDLService.java以下:
package com.styleflying.AIDL; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; public class mAIDLService extends Service{ private static final String TAG = "AIDLService"; private void Log(String str){ Log.i(TAG,"----------" + str + "----------"); } public void onCreate(){ Log("service created"); } public void onStart(Intent intent, int startId){ Log("service started id = " + startId); } public IBinder onBind(Intent t){ Log("service on bind"); return mBinder; } public void onDestroy(){ Log("service on destroy"); super.onDestroy(); } 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 mInterface.Stub mBinder = new mInterface.Stub() { public void invokTest() throws RemoteException { // TODO Auto-generated method stub Log.e(TAG, "remote call from client! current thread id = " + Thread.currentThread().getId()); } }; }
注意onBind()函數,返回了mBinder,而mBinder實現了mInterface.Stub,實現了mInterface接口,執行了打印log的操做。
整個交互流程以下:
1)客戶端經過綁定服務,獲取了服務的句柄(本地代理對象);
2)客戶端執行onClick(),調用本地代理對象的invokTest()函數,本地代理對象調用mRemote.transact()發出遠程調用請求(見 mInterface.java);
3)服務端響應onTransact()執行this.invokTest(),並將執行結果返回;
因爲客戶端只和本地代理對象即服務句柄通訊,由代理對象進行真正的IPC操做,因此對客戶端來講,IPC過程是透明的,調用遠程操做如同調用本地操做同樣。在客戶端調用transact()時,會將服務描述DSCRIPTION寫入到data裏,在客戶端onTransact時會驗證,若是兩個不同,則不能通訊。而DSCRIPTION是根據mInterface包名和接口名自動生成的,這就是爲何兩個工程裏的mInterface.aidl要在同一個包的緣由。
在這個過程當中,mInterface.aidl起到了橋樑的做用,規定統一了客戶端和服務端的通訊接口,使得客戶端和服務端得以成功的通訊。
具體的通訊transact和onTransact的過程也就是利用Binder驅動通訊的過程,在這裏就很少敘述。
最後補上兩個工程的AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.styleflying" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".AIDL.mAIDLActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="8" /> </manifest>
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.styleflying.AIDL" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:name=".mAIDLService"> <intent-filter> <action android:name="com.styleflying.AIDL.service" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> <uses-sdk android:minSdkVersion="8" /> </manifest>