AIDL的作用
AIDL (Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。
选择AIDL的使用场合
官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。
如果不需要进行不同应用程序间的并发通信(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不需要处理多线程的,则implement your interface using a Messenger。无论如何,在使用AIDL前,必须要理解如何绑定service——bindService。
AIDL服务器端
步骤一、创建aidl文件
AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。
其中对于Java编程语言的基本数据类型 (int, long, char, boolean等),String和CharSequence,集合接口类型List和Map,不需要impor语句。
而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。AIDL允许传递实现Parcelable接口的类,需要import.
需要特别注意的是,对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
AIDL只支持接口方法,不能公开static变量
1、在remoteservice工程的src文件中添加IMyService.aidl文件
package com.zmao;
import com.zmao.Person;//自定义数据
interface IMyService {
//处理自定义数据
void savePersonInfo(in Person person);
List<Person> getAllPerson();
//处理基础数据
String get();
void set(String Str);
}
2、由于aidl处理基础数据和实现PraceLabel接口的类,所以我们在Person.java文件中添加Person类实现
package com.zmao;
import java.util.HashMap;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private String telNumber;
private int age;
public Person() {}
public Person(Parcel pl){
name = pl.readString();
telNumber = pl.readString();
age = pl.readInt();
}
public Person(String name,String Tel,int age){
this.name = name;
this.telNumber = Tel;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTelNumber() {
return telNumber;
}
public void setTelNumber(String telNumber) {
this.telNumber = telNumber;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(telNumber);
dest.writeInt(age);
}
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
PraceLabel主要实现
1) void writeToParcel(Parcel dest, int flags)将需要序列化存储的数据写入外部提供的Parcel对象dest。而看了网上的代码例子,个人猜测,读取Parcel数据的次序要和这里的write次序一致,否则可能会读错数据。具体情况我没试验过!
2) describeContents()没搞懂有什么用,反正直接返回0也可以
3) static final Parcelable.Creator对象CREATOR 这个CREATOR命名是固定的,而它对应的接口有两个方法:createFromParcel(Parcel source) 实现从source创建出JavaBean实例的功能
newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。估计本方法是供外部类反序列化本类数组使用。
3、添加文件Person.aidl文件,文件内容如下
package com.zmao;
parcelable Person;
具体为什么这样我也不是太清楚,照做就行
查看在gen文件夹下自动生成一个IMyService.java文件,我们可以看到
public static abstract class Stub extends android.os.Binder implements com.zmao.IMyService
原来Stub类就是继承于Binder类,也就是说RemoteService类和普通的Service类没什么不同,只是所返回的IBinder对象比较特别,是一个实现了AIDL接口的Binder
步骤二、创建服务器
在remoteService中实现IMyService.aidl文件中的接口
package com.zmao;
import java.util.LinkedList;
import java.util.List;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.zmao.Person;
public class RemoteService extends Service {
private LinkedList<Person> personList = new LinkedList<Person>();
private String mstr;
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IMyService.Stub mBinder = new IMyService.Stub(){
@Override
public void savePersonInfo(Person person) throws RemoteException {
if (person != null){
personList.add(person);
}
}
@Override
public List<Person> getAllPerson() throws RemoteException {
return personList;
}
@Override
public String get(){
return mstr;
}
@Override
public void set(String str){
mstr = str;
}
};
}
主要实现IMyService.aidl文件接口,并且重载public IBinder onBind(Intent intent)将接口实例 mBinder返回.
对于实现AIDL接口,官方还提醒我们:
1. 调用者是不能保证在主线程执行的,所以从一调用的开始就需要考虑多线程处理,以及确保线程安全;
2. IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。也就是IPC调用会挂起应用程序导致界面失去响应,这种情况应该考虑单独开启一个线程来处理。
3. 抛出的异常是不能返回给调用者(跨进程抛异常处理是不可取的)。
步骤三、设置Service启动的IntentFilter
在AndroidManifest.xml中添加
<service android:name="RemoteService">
<intent-filter>
<action android:name="com.zmao.IMyService" />
</intent-filter>
在客户端应用中会调用<action android:name="com.zmao.IMyService" />来进行绑定服务器,这样来启动服务器
AIDL客户端
创建testService客户端应用
步骤一、拷贝
将服务器端的文件IMyService.aidl生成的IMyService.java文件和Person.java文件,按包名拷贝到testservice工程中src/com/zmao文件夹下(此方法只适用于模拟器)
或者将IMyService.aidl和Person.aidl和Person.java按包名拷贝到testservice工程中src/com/zmao文件夹下(此方法适用于模拟器和工程和工程编译)
步骤二、在文件TestserviceActivity中调用服务器方法
package com.androidbook.test;
import java.util.List;
import com.zmao.IMyService;
import com.zmao.Person;
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.view.View;
import android.widget.Button;
public class TestserviceActivity extends Activity {
/** Called when the activity is first created. */
private IMyService myService;
private Button mbutton;
private List<Person> mPerson;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ServiceConnection mServiceConnConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
myService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
myService = IMyService.Stub.asInterface(service);
}
};
bindService(new Intent("com.zmao.IMyService"),
mServiceConnConnection, Context.BIND_AUTO_CREATE);
mbutton = (Button)findViewById(R.id.button1);
mbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try{
myService.set("ni hao niu bi");
System.out.println("client is"+myService.get());
myService.savePersonInfo(new Person("Jerry","113",0));
myService.savePersonInfo(new Person("Tom","110",1));
mPerson = myService.getAllPerson();
for(int i=0; i<mPerson.size(); i++)
{
Person pPerson;
pPerson = mPerson.get(i);
System.out.println("Age is"+ pPerson.getAge());
System.out.println("name is"+ pPerson.getName());
System.out.println("TelNumber is"+ pPerson.getTelNumber());
}
}
catch(RemoteException e){
e.printStackTrace();
}
}
});
}
}
主要调用步骤
1、实例化ServiceConnection
在public void onServiceConnected(ComponentName name, IBinder service)
中IMyService.Stub.asInterface(service);获得IMyService接口,
在public void onServiceDisconnected(ComponentName name)
将myService设为空。
2、绑定服务器
bindService(new Intent("com.zmao.IMyService"), mServiceConnConnection, Context.BIND_AUTO_CREATE);
3、调用服务器方法
如果在工程上编译
IMyService.aidl和Person.aidl和Person.java按包名拷贝到testservice工程中src/com/zmao文件夹下
在工程中编译服务器端和客户端都要在Andriod.mk添加aidl编译路径
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/zmao/IMyService.aidl
下载代码地址:点击打开链接