關於AIDL的介紹及實現步驟等請參考:
http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html
本篇文章只是用一個實例來分析AIDL的實現。
本示例實現的是:AIDL客戶端通過AIDL接口獲取AIDL服務端中提供的webPage信息,下面詳述AIDL通信的實現步驟:
一、編寫服務端代碼
1. 首先編寫AndroidManifest.xml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? 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文件:
1
2
3
4
|
package com.braincol.aidl.service; interface RemoteWebPage
{ String
getCurrentPageUrl(); } |
可以看到內容很簡單,該文件中包含一個RemoteWebPage 接口,並且接口中只有getCurrentPageUrl()這麼一個方法,後面的客戶端將通過這裏提供的getCurrentPageUrl()方法獲取想要的信息。
3.生成RemoteWebPage.java文件
保存並編譯該工程(在eclipse中編譯)會看到 gen/ 目錄下的com.braincol.aidl.service包下出現了一個RemoteWebPage.java文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
/* *
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的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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文件,然後客戶端就可以通過該接口來訪問服務端提供的方法了。下面是客戶端的只要代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
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()斷開連接。連接客戶端的相關的代碼爲:
1
2
|
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對象):