关于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"?>
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{
}
}
}
|
这样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对象):