Android AIDL
Android系统中,各应用程序运行在各自的进程中,无法直接进行交互。为了实现进程间的通讯(IPC),Android提供了AIDL
Server方法进行处理。
AIDL(android
Interface defaultion language)主要分为服务端和客户端两个方面。而服务端主要是继承了Server。可以将AIDL服务端看作是个特殊的Service。
使用AIDL时,并非支持所有的数据类型。AIDL只支持以下六种数据类型:
1.
基本数据类型(void,int,char,long,double,boolean等基本数据类型)
2.
String和CharSequence;
3.
List:其中只支持ArrayList,而ArrayList中的每个元素都必须被AIDL所支持。
4.
Map:其中只支持HashMap,而HashMap中的每个元素都必须被AIDL所支持,包括Key和Value。
5.
Parcelable:支持的对象必须是经过Parcelable序列化的对象。
6.
AIDL:支持AIDL本身。
一.简单数据类型的AIDL
AIDL分为服务端和客户端两个方面。
服务端:创建一个Service,用来监听客户端的调用,并用来实现AIDL的接口函数。
客户端:绑定Service,并Service返回的Binder对象转换为AIDL接口。
创建过程:
1.
创建AIDL,并声明接口信息:
package
com.mzzhang.serviceforaidl.aidlTest;
interface
InvokeAidl {
int printInt();
String
printString(int a,String
b);
}
创建了一个文件后缀名为.ail的AIDL文件,该文件信息包括Package,interface和相关函数。其中值得注意的是该文件最好在单独的Package中,因为需要将该整个包copy到相应调用该服务的客户端中。
当创建完该AIDL文件后,在gen中会自动生成相对应的.java文件。
该文件中自动生成了一个静态的Stub类。而该类继承了Binder并实现了.aidl中定义的接口。
public static abstract
class
Stub
extends
android.os.Binder
implements com.mzzhang.serviceforaidl.aidlTest.InvokeAidl
从该类的定义可以看出,Serviece是通过Stub来实现该AIDL。而Stub是继承了Binder,所有AIDL最终是通过Binder进行进程通讯的。
2.
服务端Service的实现:
定义完AIDL接口后,需要创建Service来实现该接口。
package com.mzzhang.serviceforaidl;
import java.util.List;
import com.mzzhang.serviceforaidl.aidlTest.InvokeAidl.Stub;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MainService extends Service {
private
static
final
String
TAG = "MainService";
private
InvokeBinder
_invokeBinder;
private
int
_times
= 0;
@Override
public
void
onCreate() {
super.onCreate();
_invokeBinder
=
new
InvokeBinder();
}
@Override
public
IBinder
onBind(Intent intent) {
return
_invokeBinder;
}
public
class
InvokeBinder
extends
Stub{
@Override
public
int
printInt()
throws
RemoteException
{
Log.d(TAG, "this is print int, the times is
:" +
_times);
_times++;
return
_times;
}
@Override
public
String
printString(int a, String b) throws RemoteException {
String s = "a is " + a +" b is "+b;
Return
s;
}
}
}
创建了一个MainService后,由于该Service为其他进行提供服务,因此需要采用BindService的方法。并在OnBind中return一个Binder对象。通过刚才对Stub的分析可以得知Stub是AIDL生成,并继承Binder对象。因此定义了一个继承Stub对象,并实现了该静态对象类的方法。因此onBind返回的是个Stub。
3.
注册AndroidMainFest.xml中信息
该Service为其他进程提供服务和数据,因此其他进程无法采用显式启动的方式访问到该Service因此需要在AndroidMainFext.xml中定义相关的Intet-filter信息,以提供其他进程进行访问。
<service
android:name="com.mzzhang.serviceforaidl.MainService">
<intent-filter>
<action android:name="com.mzzhang.serviceforaidl.service"/>
intent-filter>
service>
到此Service客户端的操作基本完成。
4.
Copy AIDL到需调用该AIDL的进程中:
客户端中采用的是BindServer的方法启动该应用,并返回了一个stub对象。但在客户端中对该Stub对象却是一无所知。因此需要通过将AIDL对应的文件copy到该客户端中,在客户端中自动生成一个Stub对象。因此在第1步中,提及最好将AIDL放在单独的一个包中。
5.
客户端的实现:
package com.mzzhang.aidlcustomer;
import com.mzzhang.serviceforaidl.aidlTest.InvokeAidl;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity{
private
static
final
String
TAG = "MainActivity";
private
InvokeAidl
_invokeAidl
;
Button btn_get;
Button btn_show;
TextView tv_show;
StringBuilder sb;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_get
= (Button)
findViewById(R.id.btn_get);
btn_show
= (Button)
findViewById(R.id.btn_show);
tv_show
= (TextView)
findViewById(R.id.tv_show);
}
@Override
protected
void
onResume() {
super.onResume();
sb = new StringBuilder();
btn_get.setOnClickListener(new
OnClickListener()
{
@Override
public
void
onClick(View
v) {
Intent intent = new Intent();
intent.setAction("com.mzzhang.serviceforaidl.service");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
btn_show.setOnClickListener(new
OnClickListener()
{
@Override
public
void
onClick(View
v) {
try
{
sb.append(_invokeAidl.printInt() + "\n");
sb.append(_invokeAidl.printString(1, "test")+ "\n");
tv_show.setText(sb.toString());
} catch (RemoteException
e) {
//
TODO
Auto-generated catch
block
e.printStackTrace();
}
}
});
}
private
ServiceConnection
conn
=
new
ServiceConnection()
{
@Override
public
void
onServiceDisconnected(ComponentName
name) {
}
@Override
public
void
onServiceConnected(ComponentName
name, IBinder service) {
_invokeAidl
=
InvokeAidl.Stub.asInterface(service);
}
};
}
在该客户端中采用的是bindService的方法,返回了一个ServiceConnect。
Intent intent = new Intent();
intent.setAction("com.mzzhang.serviceforaidl.service");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
并在ServiceConnection中进行绑定。而采用的绑定方法为将IBinder转换为AIDL的Interface。InvokeAidl.Stub.asInterface(service)。
绑定后,直接通过绑定的对象调用该接口函数进行通信。
以上,便可实现一个简单的IPC。除了能传输基本数据类型的AIDL外,AIDL也可传输对象类型。
二.在进程中传递对象数据
1.
对象序列化
在进程中进行对象传递时,需要对对象进行序列化过程后,才能进行传递。例如该案例中创建一个Person对象类,并对其进行序列化,具体系列化过程见:。
2.
创建parcelable AIDL
由于AIDL并不支持自定义对象类,而对象类只实现Pacelable因此还需要创建一个parcelable的AIDL,供AIDL调用。
package
com.mzzhang.serviceforaidl.model;
parcelable Person;
3.
添加对象信息到AIDL中。
package
com.mzzhang.serviceforaidl.aidlTest;
import
com.mzzhang.serviceforaidl.model.Person;
interface
InvokeAidl {
void setPerson(in Person person);
Person getPerson();
}
其中需要import该person类的package位置。
我们需要注意的是,在使用对象为参数时,用到了in和out或inOut三种类型。In表示输入类型,out表示输出类型,inOut表示输入和输出类型。
4.
Copy AIDL与Person类
将AIDL和Person类都copy到需要调用的进程中。包括Person类创建的AIDL。
5.
在客户端中实现Person功能
_invokeAidl.setPerson(_person);
_person
= _invokeAidl.getPerson();
三.远程回调:
在使用进程时,我们需要考虑到的一点就是进程间的回调。通过Service创建一个回调方法供进程进行回调。
1.
创建回调AIDL。
package
com.mzzhang.serviceforaidl.aidlTest;
interface
IPersonListener {
void onSomeNeedCallBack();
}
2. 在AIDL中定义回调方法。
package
com.mzzhang.serviceforaidl.aidlTest;
import
com.mzzhang.serviceforaidl.aidlTest.IPersonListener;
interface
InvokeAidl {
void registerListener();
void unRegisterListener();
}
在该AIDL中创建了两个函数方法,分别为register和unRegister。
3.
Service操作
采用RemoteCallbackList方法进行操作。
privateRemoteCallbackList
_personListener;
并在Stub中注册监听事件
@Override
public void registerListener(IPersonListener
listener) throws RemoteException {
_personListener.register(listener);
Log.d(TAG, " some one call the register
Listener");
}
@Override
public void unRegisterListener(IPersonListener
listener) throws RemoteException {
_personListener.unregister(listener);
Log.d(TAG, " some one call the unRegister
Listener");
}
通过遍历RemoteCallbackList寻找Listener并进行操作。
int i = _personListener.beginBroadcast();
Log.d(TAG,
"the thread time is
" +
i);
while (i > 0) {
i--;
try
{
IPersonListener
l = _personListener.getBroadcastItem(i);
if(l!=null){
_person.setId(5555);
l.onSomeNeedCallBack(_person);
Log.d(TAG,
"some things to be
call!");
}
} catch (RemoteException
e) {
e.printStackTrace();
}
}
_personListener.finishBroadcast();
RemoteCallbackList是Android系统专门提供用于删除跨进程Listener的接口的。而该Listener接口在Android的底层是公用一个Binder对象,所有可实现在不同的进程中的Listener是同一个,从而避免了服务端和客户端注册和解绑的Listener为不同的对象。
四.权限设定
在默认的情况中,所有的AIDL都是可调用的。但在某些情况下,总是希望只有个别获得权限的进程才能使用,因此在使用AIDL时,我们需要为AIDL添加权限。
在服务端AndroidManifest.xml中添加权限。
<permission
android:name = "com.mzzhang.serviceforaidl.permission_server"
android:protectionLevel="normal"/>
1.
在服务端OnBinder中验证:
@Override
public
IBinder
onBind(Intent intent) {
int check =
checkCallingOrSelfPermission("com.mzzhang.serviceforaidl.permission_server");
if(check ==
PackageManager.PERMISSION_DENIED)
return
null;
return
_invokeBinder;
}
若客户端设定的权限为com.mzzhang.serviceforaidl.permission_server,则验证通过,否则验证不通过。
客户端权限:
在客户端AndroidMainfest.xml中设置该权限。
<uses-permission android:name="com.mzzhang.serviceforaidl.permission_server"/>
2.
在Stub中使用onTransact方法验证。该方法除了验证Permission外,还可通过验证getCallingUid和getCallingPid方法。
@Override
public
boolean
onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
int
check
=
checkCallingOrSelfPermission("com.mzzhang.serviceforaidl.permission_server");
if(check ==
PackageManager.PERMISSION_DENIED)
return
false;
String packageName
=
null;
String[] packages =
getPackageManager().getPackagesForUid(getCallingUid());
if(packages!=null && packages.length > 0){
if(!packages[0].startsWith("com.mzzhang.serviceforaidl"))
return
false;
}
return
super.onTransact(code, data, reply, flags);
}
五.注意事项
1.
在使用List传递时最好使用CopyOnWriteArrayList来代替ArrayList该方法父类为list所有也支持AIDL。
2.
由于Service和Activity都属于UI线程,所有在操作相对较耗时的操作时,应该采用线程进行操作。
3.
对象序列化后,需要将对象再进行一次AIDL,其中parcelable为小写。
简单示例代码见:http://download.csdn.net/detail/zhezizhang/9633153