1. aidl简单介绍
AIDL全称是android interface define language, 它主要是为了实现RPC, 使得进程之间的数据传输像函数调用一样简单,在android系统中使用aidl常见的场景就是远程服务向客户端应用程序提供功能性接口,客户端调用这些接口就可以和远程服务进行通信。而这些接口即需要使用aidl来定义出来。
2. aidl接口的参数修饰符号
aidl中的接口传递的数据,简单数据类型可以直接传递,而对于其它数据类型,比如自定义数据结构或者或者由aidl生成的接口则有其它特殊的要求。以下是aidl中支持的数据类型:
- Java的原生类型
- String, CharSequence
- List, Map对象的元素必须是aidl支持的数据类型,如果是非原生数据类型,还必须import 对应的类,并且需要加上稍后提到的参数修饰符号in, out或者inout
- 由aidl生成的接口作为参数,但需要import接口
- 实现Parcelable接口的自定义类,需要import这个类
aidl 中有以下几个参数修饰符号:
- in 表示是由客户端传输到远程服务端的数据
- out 表示是由远程服务端传输到客户端的数据
- inout 表示由客户端传输到远程服务端的数据,而服务端可以修改这个数据并且传回客户端
3. 以下是一个简单的例子,定义一个.aidl文件,在service中实现aidl文件中定义的接口,以及如何使用这些接口。
3.1 首先我们定义一个Person.aidl,因为需要在进程之间传递数据,所以定义的这个Person需要implements Parcelable接口,从而实现序列化
package com.example.testapp;
parcelable Person;
3.2 定义Person.java, implements Parcelable接口
package com.example.testapp;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable{
public int mAge;
public int mWeight;
public Person() {
mAge = 0;
mWeight = 0;
}
public Person(int age, int weight) {
mAge = age;
mWeight = weight;
}
public Person(Parcel source) {
readFromParcel(source);
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel arg0, int arg1) {
// TODO Auto-generated method stub
arg0.writeInt(mAge);
arg0.writeInt(mWeight);
}
public void readFromParcel(Parcel _reply) {
// TODO Auto-generated method stub
mAge = _reply.readInt();
mWeight = _reply.readInt();
}
public static final Parcelable.Creator<Person> CREATOR=new Creator<Person>() {
@Override
public Person[] newArray(int size) {
return new Person[size];
}
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
};
}
3.3 定义DataListener.aidl,这个回调接口需要调用者去实现作为参数传输到远程服务(见RemoteService.aidl的register接口),可以看到DataListener中定义一个dataChange的方法,Person作为参数。之前做项目的时候遇到了这样一个问题,原以为dataChange函数的Person参数是远程服务传输到调用者,只需要用out修饰符就行了,但是实际结果证明,在服务器的Person并不能传送到调用者。需要使用inout修饰符
package com.example.testapp;
import com.example.testapp.Person;
interface DataListener {
void dataChange(inout Person p);
}
3.4 定义RemoteService.aidl, 这里我们使用了in修饰符,调用者传输数据到远程服务;inout调用者传输数据到远程服务,并且远程服务可修改这个数据
package com.example.testapp;
import com.example.testapp.DataListener;
import com.example.testapp.Person;
interface RemoteService {
void print(in Person p);
void modify(inout Person p);
void register(DataListener listener);
}
3.5 定义MyRemoteService.java实现在RemoteService.aidl中提供的接口
package com.example.testapp;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyRemoteService extends Service{
public static final String TAG = "MyRemoteService";
private class MyServiceImpl extends RemoteService.Stub{
@Override
public void print(Person p) throws RemoteException {
Log.d(TAG, "MyRemoteService, process id:" + android.os.Process.myPid());
Log.d(TAG, "age:" + p.mAge + "; weight:" + p.mWeight);
}
@Override
public void modify(Person p) throws RemoteException {
p.mAge = 30;
p.mWeight = 140;
}
@Override
public void register(DataListener listener) throws RemoteException {
// TODO Auto-generated method stub
Person p = new Person(23, 120);
listener.dataChange(p);
}
}
@Override
public IBinder onBind(Intent arg0) {
//return implementation of AIDL
Log.e(TAG, "onBind");
return new MyServiceImpl();
}
@Override
public void onDestroy() {
Log.e(TAG, "Release MyService");
super.onDestroy();
}
}
3.5 定义一个MainActivity.java去bind service, 调用接口
package com.example.testapp;
public class MainActivity extends Activity {
private static String TAG = "MainActivity";
private Button btn = null;
private RemoteService iService = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.startServiceBtn);
btn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
bindMyService();
}
});
}
private void bindMyService(){
<span style="white-space:pre"> </span>Intent intent = new Intent("com.example.testapp.startService");
<span style="white-space:pre"> </span>Log.d(TAG, "bindMyService, process id:" + android.os.Process.myPid());
<span style="white-space:pre"> </span>bindService(intent, conn, Context.BIND_AUTO_CREATE);
<span style="white-space:pre"> </span>}
private ServiceConnection conn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//return AIDL object,then call methods of AIDL
iService = RemoteService.Stub.asInterface(service);
try {
Log.d(TAG, "onServiceConnected, process id:" + android.os.Process.myPid());
Person p = new Person(40, 143);
iService.print(p);
iService.modify(p);
Log.d(TAG, "age:" + p.mAge + "; weight:" + p.mWeight);
iService.register(new DataListener.Stub(){
@Override
public void dataChange(Person p) throws RemoteException {
// TODO Auto-generated method stub
Log.d(TAG, "person.age:" + p.mAge);
Log.d(TAG, "person.weight:" + p.mWeight);
}
});
} catch (RemoteException e) {
Log.d(TAG, "Error while call on iService!");
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d(TAG, "release iService");
}
};
}
运行程序, 打印结果如下:
可见modify方法修改了由调用者传输过去的数据,而register方法注册到远程服务的DataListener, 数据也正确的传输到调用者了。
09-21 22:08:16.561: D/MainActivity(24293): bindMyService, process id:24293
09-21 22:08:16.646: D/MainActivity(24293): onServiceConnected, process id:24293
09-21 22:08:16.648: D/MainActivity(24293): age:30; weight:140
09-21 22:08:16.650: D/MainActivity(24293): person.age:23
09-21 22:08:16.650: D/MainActivity(24293): person.weight:120
09-21 22:08:16.643: E/MyRemoteService(24329): onBind
09-21 22:08:16.648: D/MyRemoteService(24329): MyRemoteService, process id:24329
09-21 22:08:16.648: D/MyRemoteService(24329): age:40; weight:143
*****************************************************************************************************************************************
在3.3 提到了在dataChange函数里,如果使用out修饰符,远程服务的数据不能传输到调用者,以下是截取了自定生成文件DataListener.java的部分代码.
- 使用inout修饰
@Override public void dataChange(com.example.testapp.Person p) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((p!=null)) {
_data.writeInt(1);
<span style="color:#ff6666;">p.writeToParcel(_data, 0);</span>
}
else {
_data.writeInt(0);
}
<span style="color:#ff6666;">mRemote.transact(Stub.TRANSACTION_dataChange, _data, _reply, 0);</span>
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case TRANSACTION_dataChange:
{
data.enforceInterface(DESCRIPTOR);
com.example.testapp.Person _arg0;
if ((0!=data.readInt())) {
<span style="color:#ff6666;">_arg0 = com.example.testapp.Person.CREATOR.createFromParcel(data);</span>
}
else {
_arg0 = null;
}
<span style="color:#ff6666;">this.dataChange(_arg0);</span>
- 使用out 修饰
@Override public void dataChange(com.example.testapp.Person p) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
<span style="color:#ff6666;">mRemote.transact(Stub.TRANSACTION_dataChange, _data, _reply, 0);</span>
通过对比上面的代码,可以看到使用inout修身的参数Person, 会调用writeToParcel写入_data, 然后调用onTransact函数,最后转换成了Person对象,最终调到了this.dataChange(_arg0)。 而对于out修饰的参数,并没有经过这一系列的转化,所以调用者没有获取到远程服务传输的数据。