關於AIDL的介紹及實現步驟等請參考:
http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html
本篇文章只是用一個執行個體來分析AIDL的實現。
本樣本實現的是:AIDL用戶端通過AIDL介面擷取AIDL服務端中提供的webPage資訊,下面詳述AIDL通訊的實現步驟:
一、編寫服務端代碼
1. 首先編寫AndroidManifest.xml檔案:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.braincol.aidl.service" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:name="RemoteService"> <intent-filter> <action android:name="com.braincol.aidl.remote.webpage"/> </intent-filter> </service> </application></manifest>
可以看到服務端的包名為:com.braincol.aidl.service,且該服務端只需一個service組件提供AIDL服務,service組件的名稱為RemoteService,這是待會要實現的Service子類。其中<action android:name="com.braincol.aidl.remote.webpage"/> ,指定了action名稱為"com.braincol.aidl.remote.webpage", 用戶端會通過該action的名稱來找到並串連該服務端。
2. 建立RemoteWebPage.aidl檔案
在包com.braincol.aidl.service下建立RemoteWebPage.aidl檔案:
package com.braincol.aidl.service; interface RemoteWebPage { String getCurrentPageUrl(); }
可以看到內容很簡單,該檔案中包含一個RemoteWebPage 介面,並且介面中只有getCurrentPageUrl()這麼一個方法,後面的用戶端將通過這裡提供的getCurrentPageUrl()方法擷取想要的資訊。
3.產生RemoteWebPage.java檔案
儲存並編譯該工程(在eclipse中編譯)會看到 gen/ 目錄下的com.braincol.aidl.service包下出現了一個RemoteWebPage.java檔案:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: F:\\workspace\\android\\AIDL-simple\\AIDLService\\src\\com\\braincol\\aidl\\service\\RemoteWebPage.aidl */package com.braincol.aidl.service;public interface RemoteWebPage extends android.os.IInterface{ /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.braincol.aidl.service.RemoteWebPage { private static final java.lang.String DESCRIPTOR = "com.braincol.aidl.service.RemoteWebPage"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.braincol.aidl.service.RemoteWebPage interface, * generating a proxy if needed. */ public static com.braincol.aidl.service.RemoteWebPage asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.braincol.aidl.service.RemoteWebPage))) { return ((com.braincol.aidl.service.RemoteWebPage)iin); } return new com.braincol.aidl.service.RemoteWebPage.Stub.Proxy(obj); } public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getCurrentPageUrl: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getCurrentPageUrl(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.braincol.aidl.service.RemoteWebPage { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } public java.lang.String getCurrentPageUrl() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getCurrentPageUrl, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getCurrentPageUrl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String getCurrentPageUrl() throws android.os.RemoteException;}
這個檔案是Android SDK工具根據RemoteWebPage.aidl自動產生的,不要嘗試著去修改該檔案(改了也白改)。可以看到RemoteWebPage介面內包含了一個名為Stub的抽象的內部類,該類聲明了RemoteWebPage.aidl中描述的方法getCurrentPageUrl(),並且還定義了少量的輔助方法Stub還定義了少量的輔助方法,尤其是asInterface(),通過它或以獲得IBinder(當applicationContext.bindService()成功調用時傳遞到用戶端的onServiceConnected())並且返回用於調用IPC方法的介面執行個體,更多細節參見Calling an IPC Method。
4. 編寫RemoteService.java
為了實現AIDL通訊,必須在RemoteService類中實現RemoteWebPage.Stub介面,然後RemoteWebPage.Stbu內的相關方法,下面是RemoteService.java的代碼:
package com.braincol.aidl.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;/** * * @author briancol * @description 提供service * */public class RemoteService extends Service { private final static String TAG = "RemoteService"; @Override public IBinder onBind(Intent intent) { Log.i(TAG, "OnBind"); return new MyBinder(); } private class MyBinder extends RemoteWebPage.Stub{ @Override public String getCurrentPageUrl() throws RemoteException{ return "http://www.cnblogs.com/hibraincol/"; } }}
這樣MyBinder就是一個RemoteWebPage.Stub類得子類,這樣就可以通過RemoteService向用戶端暴露AIDL介面了(MyBinder )。現在,如果用戶端(比如一個Activity)調用bindService()來串連該服務端(RemoteService) ,用戶端的onServiceConnected()回呼函數將會獲得從服務端(RemoteService )的onBind()返回的MyBinder對象。
在這裡總結下服務端的編寫流程:
1. 建立.aidl檔案:
該檔案(YourInterface.aidl)定義了用戶端可用的方法和資料的介面
2. 實現這個介面:
Android SDK將會根據你的.aidl檔案產生AIDL介面。產生的介面包含一個名為Stub的抽象內部類,該類聲明了所有.aidl中描述的方法,你必須在代碼裡繼承該Stub類並且實現.aidl中定義的方法。
3.向用戶端公開服務端的介面:
實現一個Service,並且在onBinder方法中返回第2步中實現的那個Stub類的子類(實作類別)。
至此,服務端的代碼就編寫完成了。 下面開始編寫用戶端。
二、編寫用戶端代碼
因為用戶端和服務端不在同一個進程(應用程式)中,那麼用戶端也必須在src/目錄下擁有和服務端同樣的一份.aidl檔案的拷貝(這樣的同樣是指:包名、類名、內容完全一樣,可以把用戶端的.aidl檔案理解為代理),這樣用戶端將會通過這個RemoteWebPage.aidl檔案在gen/目下產生和服務端一樣的RemoteWebPage.java檔案,然後用戶端就可以通過該介面來訪問服務端提供的方法了。下面是用戶端的只要代碼:
package com.braincol.aidl.client;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.TextView;import com.braincol.aidl.service.RemoteWebPage;public class ClientActivity extends Activity implements OnClickListener { private final static String TAG="ClientActivity"; TextView textView ; Button btn_bind ; Button btn_getAllInfo; String actionName = "com.braincol.aidl.remote.webpage"; RemoteWebPage remoteWebPage=null; String allInfo = null; boolean isBinded=false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView) findViewById(R.id.textView); btn_bind = (Button) findViewById(R.id.btn_bind); btn_getAllInfo = (Button)findViewById(R.id.btn_allinfo); btn_getAllInfo.setEnabled(false); btn_bind.setOnClickListener(this); btn_getAllInfo.setOnClickListener(this); } @Override protected void onPause(){ super.onPause(); Log.d(TAG,"onPause"); if(isBinded){ Log.d(TAG,"unbind"); unbindService(connection); } } private class MyServiceConnection implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "建立串連..."); remoteWebPage = RemoteWebPage.Stub.asInterface(service); if(remoteWebPage==null){ textView.setText("bind service failed!"); return; } try { isBinded=true; btn_bind.setText("斷開"); textView.setText("已串連!"); allInfo = remoteWebPage.getCurrentPageUrl(); btn_getAllInfo.setEnabled(true); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG, "onServiceDisconnected..."); } } MyServiceConnection connection = new MyServiceConnection(); @Override public void onClick(View v) { if(v==this.btn_bind){ if(!isBinded){ Intent intent = new Intent(actionName); bindService(intent, connection, Context.BIND_AUTO_CREATE); }else{ Log.i(TAG, "中斷連線..."); unbindService(connection); btn_getAllInfo.setEnabled(false); btn_bind.setText("串連"); isBinded = false; textView.setText("已中斷連線!"); } }else if(v==this.btn_getAllInfo){ textView.setText(allInfo); } }}
上面的代碼中類MyServiceConnection實現了ServiceConnection類,在MyServiceConnection類的onServiceConnected方法中通過RemoteWebPage.Stub.asInterface(service)擷取到遠端服務端的RemoteWebPage介面對象remoteWebPage,這樣就可以通過remoteWebPage.getCurrentPageUrl()擷取到服務端提供的相關的資訊。用戶端通過bindService()方法綁定遠程服務端,通過unbindService()中斷連線。串連用戶端的相關的代碼為:
Intent intent = new Intent(actionName); bindService(intent, connection, Context.BIND_AUTO_CREATE);
用戶端就是通過actionName(com.braincol.aidl.remote.webpage)來找到服務端。
下面總結下用戶端的編寫流程:
1. 在 src/ 目錄下包含.adil檔案。
2. 聲明一個IBinder
介面(通過.aidl檔案產生的)的執行個體。
3. 實現ServiceConnection
.
4. 調用Context.bindService()綁定你的ServiceConnection實作類別的對象(也就是遠程服務端)。
5. 在onServiceConnected()
方法中會接收到IBinder對象(也就是服務端),調用YourInterfaceName.Stub.asInterface((IBinder)service)
將傳回值轉換為YourInterface類型。
6. 調用介面中定義的方法,並且應該總是捕獲串連被打斷時拋出的DeadObjectException異常,這是遠端方法可能會拋出唯一異常。
7. 調用Context.unbindService()方法中斷連線。
下面給出本樣本源碼的:
http://download.csdn.net/detail/Niosm/3593187
在附一個稍微複雜點的例子(通過IPC傳遞Parcelable對象):
http://download.csdn.net/detail/Niosm/3593376