本文提供了一個關於AIDL使用的簡單易懂的例子,分為用戶端和服務端兩部分,分別為用戶端和服務端建立一個eclipse工程,實現了從用戶端向服務端發送請求,服務端列印log的功能。
用戶端和服務端的源碼結構如下:
注意,由於用戶端和服務端的aidl檔案所在包名必須一樣,而兩個包名一樣的程式在安裝時會產生衝突,所以這裡用了一個技巧,在用戶端工程的AndroidManifest.xml裡把包名指定為com.styleflying,所以大家就會看到gen目錄下的R.java所在的包是com.styleflying而不是com.styleflying.AIDL
本文
現在用戶端和服務端工程分別建立一個aidl介面,所在包和檔案名稱必須一樣。兩個aidl介面是一樣的,內容如下:
package com.styleflying.AIDL;<br />interface mInterface{<br />void invokTest();<br />}
自動編譯產生.java檔案如下:
/*<br /> * This file is auto-generated. DO NOT MODIFY.<br /> * Original file: G://workspace//AidlDemo_client//src//com//styleflying//AIDL//mInterface.aidl<br /> */<br />package com.styleflying.AIDL;<br />public interface mInterface extends android.os.IInterface<br />{<br />/** Local-side IPC implementation stub class. */<br />public static abstract class Stub extends android.os.Binder implements com.styleflying.AIDL.mInterface<br />{<br />private static final java.lang.String DESCRIPTOR = "com.styleflying.AIDL.mInterface";<br />/** Construct the stub at attach it to the interface. */<br />public Stub()<br />{<br />this.attachInterface(this, DESCRIPTOR);<br />}<br />/**<br /> * Cast an IBinder object into an com.styleflying.AIDL.mInterface interface,<br /> * generating a proxy if needed.<br /> */<br />public static com.styleflying.AIDL.mInterface asInterface(android.os.IBinder obj)<br />{<br />if ((obj==null)) {<br />return null;<br />}<br />android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);<br />if (((iin!=null)&&(iin instanceof com.styleflying.AIDL.mInterface))) {<br />return ((com.styleflying.AIDL.mInterface)iin);<br />}<br />return new com.styleflying.AIDL.mInterface.Stub.Proxy(obj);<br />}<br />public android.os.IBinder asBinder()<br />{<br />return this;<br />}<br />@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException<br />{<br />switch (code)<br />{<br />case INTERFACE_TRANSACTION:<br />{<br />reply.writeString(DESCRIPTOR);<br />return true;<br />}<br />case TRANSACTION_invokTest:<br />{<br />data.enforceInterface(DESCRIPTOR);<br />this.invokTest();<br />reply.writeNoException();<br />return true;<br />}<br />}<br />return super.onTransact(code, data, reply, flags);<br />}<br />private static class Proxy implements com.styleflying.AIDL.mInterface<br />{<br />private android.os.IBinder mRemote;<br />Proxy(android.os.IBinder remote)<br />{<br />mRemote = remote;<br />}<br />public android.os.IBinder asBinder()<br />{<br />return mRemote;<br />}<br />public java.lang.String getInterfaceDescriptor()<br />{<br />return DESCRIPTOR;<br />}<br />public void invokTest() throws android.os.RemoteException<br />{<br />android.os.Parcel _data = android.os.Parcel.obtain();<br />android.os.Parcel _reply = android.os.Parcel.obtain();<br />try {<br />_data.writeInterfaceToken(DESCRIPTOR);<br />mRemote.transact(Stub.TRANSACTION_invokTest, _data, _reply, 0);<br />_reply.readException();<br />}<br />finally {<br />_reply.recycle();<br />_data.recycle();<br />}<br />}<br />}<br />static final int TRANSACTION_invokTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);<br />}<br />public void invokTest() throws android.os.RemoteException;<br />}<br />
用戶端的mAIDLActivity.java如下:
package com.styleflying.AIDL;<br />import android.app.Activity;<br />import android.content.ComponentName;<br />import android.content.Context;<br />import android.content.Intent;<br />import android.content.ServiceConnection;<br />import android.os.Bundle;<br />import android.os.IBinder;<br />import android.os.RemoteException;<br />import android.util.Log;<br />import android.view.View;<br />import android.view.View.OnClickListener;<br />import android.widget.Button;<br />import android.widget.Toast;<br />import com.styleflying.R;<br />public class mAIDLActivity extends Activity {</p><p>private static final String TAG = "AIDLActivity";<br />private Button btnOk;<br />private Button btnCancel;<br />private Button btnCallBack;</p><p>private void Log(String str){<br />Log.d(TAG,"----------" + str + "----------");<br />}</p><p>mInterface mService;<br />private ServiceConnection mConnection = new ServiceConnection(){<br />public void onServiceConnected(ComponentName className,<br />IBinder service){<br />Log("connect service");<br />mService = mInterface.Stub.asInterface(service);<br />}</p><p>public void onServiceDisconnected(ComponentName className){<br />Log("disconnect service");<br />mService = null;<br />}<br />};</p><p> /** Called when the activity is first created. */<br /> @Override<br /> public void onCreate(Bundle savedInstanceState) {<br /> super.onCreate(savedInstanceState);<br /> setContentView(R.layout.main);</p><p> btnOk = (Button)findViewById(R.id.btn_ok);<br /> btnCancel = (Button)findViewById(R.id.btn_cancel);<br /> btnCallBack = (Button)findViewById(R.id.btn_callback);</p><p> btnOk.setOnClickListener(new OnClickListener(){<br /> public void onClick(View v){<br /> Bundle args = new Bundle();<br /> Intent intent = new Intent("com.styleflying.AIDL.service");<br /> intent.putExtras(args);<br /> bindService(intent,mConnection,Context.BIND_AUTO_CREATE);<br /> }<br /> });</p><p> btnCancel.setOnClickListener(new OnClickListener(){<br /> public void onClick(View v){<br /> unbindService(mConnection);<br /> }<br /> });<br /> btnCallBack.setOnClickListener(new OnClickListener(){<br /> public void onClick(View v){<br /> try{<br /> Log.i(TAG,"current Thread id = " + Thread.currentThread().getId());<br /> mService.invokTest();<br /> }<br /> catch(RemoteException e){</p><p> }<br /> }<br /> });</p><p> }<br />}
用戶端在執行bindService的時候,成功綁定服務之後,會回調mConnection的onServiceConnected(),並且傳回了服務端的通訊介面IBinder,此IBinder即服務onBind()時返回的IBinder,詳見mAIDLService.java。
在onServiceConnected(),用戶端成功擷取了服務端通訊介面,實際上是本地代理對象,該對象存在於用戶端進程空間,用戶端只和代理對象互動,真正的IPC通訊是本地代理對象和服務端的通訊。
mAIDLService.java如下:
package com.styleflying.AIDL;<br />import android.app.Service;<br />import android.content.Intent;<br />import android.os.IBinder;<br />import android.os.Looper;<br />import android.os.RemoteException;<br />import android.util.Log;<br />import android.widget.Toast;<br />public class mAIDLService extends Service{<br />private static final String TAG = "AIDLService";</p><p>private void Log(String str){<br />Log.i(TAG,"----------" + str + "----------");<br />}</p><p>public void onCreate(){<br />Log("service created");<br />}</p><p>public void onStart(Intent intent, int startId){<br />Log("service started id = " + startId);<br />}</p><p>public IBinder onBind(Intent t){<br />Log("service on bind");<br />return mBinder;<br />}</p><p>public void onDestroy(){<br />Log("service on destroy");<br />super.onDestroy();<br />}</p><p>public boolean onUnbind(Intent intent){<br />Log("service on unbind");<br />return super.onUnbind(intent);<br />}</p><p>public void onRebind(Intent intent){<br />Log("service on rebind");<br />super.onRebind(intent);<br />}</p><p>private final mInterface.Stub mBinder = new mInterface.Stub() {<br />public void invokTest() throws RemoteException {<br />// TODO Auto-generated method stub<br />Log.e(TAG, "remote call from client! current thread id = " + Thread.currentThread().getId());<br />}<br />};<br />}<br />
注意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"?><br /><manifest xmlns:android="http://schemas.android.com/apk/res/android"<br /> package="com.styleflying"<br /> android:versionCode="1"<br /> android:versionName="1.0"><br /> <application android:icon="@drawable/icon" android:label="@string/app_name"><br /> <activity android:name=".AIDL.mAIDLActivity"<br /> android:label="@string/app_name"><br /> <intent-filter><br /> <action android:name="android.intent.action.MAIN" /><br /> <category android:name="android.intent.category.LAUNCHER" /><br /> </intent-filter><br /> </activity><br /> </application><br /> <uses-sdk android:minSdkVersion="8" /><br /></manifest> <?xml version="1.0" encoding="utf-8"?><br /><manifest xmlns:android="http://schemas.android.com/apk/res/android"<br /> package="com.styleflying.AIDL"<br /> android:versionCode="1"<br /> android:versionName="1.0"><br /> <application android:icon="@drawable/icon" android:label="@string/app_name"><br /> <service android:name=".mAIDLService"><br /> <intent-filter><br /> <action android:name="com.styleflying.AIDL.service" /><br /> <category android:name="android.intent.category.DEFAULT" /><br /> </intent-filter><br /> </service><br /> </application><br /> <uses-sdk android:minSdkVersion="8" /><br /></manifest>