转载注明出处:http://blog.csdn.net/u012861467/article/details/51684484
AIdl 中文意思是Android 接口定义语言,用于跨进程通讯定义接口用的。跨进程通讯在android中用得最多的就是Binder了,而AIDL就是配套Binder使用的一种接口定义语言,它会转化生成对应的接口文件。
Binder跨进程通讯传递的数据类型是有限制的,如下:1、JAVA的基本数据类型(int, long, char, boolean等)。不需要import包。
2、String 和 CharSequence。不需要import包。
3、集合类型 java.util.List和java.util.Map,不需要import包。但是集合里面的项的数据类型同样要满足这几个限制,AIDL接口和 实现Parcelable的复杂类都要import包。
4、AIDL 接口,需要import包,即使在同一个包下。
5、实现 android.os.Parcelable 接口的复杂类。需要import包。
注意:AIDL定义文件中方法的参数除了基本数据类型、String类型和CharSequence类型之外,其它类型都需要使用方向标签,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
例如:
String showPerson(in Person person);
下面带大家来使用一下AIDL进行跨进程通讯,这个例子的功能是创建一个远程服务,使用客户端调用服务中的方法。传递的数据有简单的数据类型也有复杂的数据类型。
下面接着开始罗,一步一步来!!!
步骤一:先定义AIDL文件
如果使用的开发工具是Android studio,那么点击选中新建工程的根目录右键--> NEW--> AIDL--> AIDL File,填写自己的aidl文件名称,点击“OK”,工具会自动帮你生成一个aidl后缀的文件,该文件是放在跟java文件夹同级的aidl文件夹下,路径跟工程的包名一致。代码如下:
package com.example.aidlservicetest;
import com.example.aidlservicetest.Person;
interface IMyServiceInterface {
// 自己定义的一些方法声明
int add(int a,int b);
// 实现Parcelable 接口的复杂类需要import包
String showPerson(in Person person);
List<Person> getPersonList(in Person person);
Map getMap(String key, in Person person);
Person getPerson();
}
把里面生成的多余的代码删掉,添加自己的一些方法声明,可以注意到不同的数据类型的写法应该遵循上面列出的限制。
定义aidl文件完成后,点击菜单栏的“Build”-->Make Project,开发工具会生成aidl文件对应的java接口文件,我们不需要去编辑它。
上面的 Person类实现了Parcelable接口。
下面补充一下实现Parcelable接口注意的地方:
先定义一个Person类实现Parcelable接口,生成的代码如下:public class Person implements Parcelable {
protected Person(Parcel in) {
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
}
}
Parcelable需要实现两个方法和一个接口:
1) void writeToParcel(Parcel dest, int flags) 把数据放到Parcel对象里面。读取变量和写入变量的顺序应该一致,不然得不到正确的结果。
2) describeContents() 通常直接返回0
3) 定义一个static final 修饰的实现android.os.Parcelable.Creator<T>接口的Creator类对象CREATOR,它的名字是固定的。
实现接口的两个方法:
createFromParcel(Parcel source) 从Parcel对象中生成实例对象。读取变量和写入变量的顺序应该一致,不然得不到正确的结果。
newArray(int size) 创建长度为size的实例对象数组。
下面是完整的Person类代码:
package com.example.aidlservicetest;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable{
private String name;//名字
private int sex;//性别
public Person(){}
//从Parcel解析出Person
protected Person(Parcel in) {
readFromParcel(in);
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
//注意读取变量和写入变量的顺序应该一致 不然得不到正确的结果
parcel.writeString(name);
parcel.writeInt(sex);
}
//注意读取变量和写入变量的顺序应该一致 不然得不到正确的结果
public void readFromParcel(Parcel source) {
name = source.readString();
sex = source.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
@Override
public String toString() {
return "name=" + name +", sex=" + sex;
}
}
因为自定义的Person类是复杂数据类型,在同一包下还需要新建一个对应的Person.aidl文件告诉系统我们需要序列化和反序列化的类型。每一个实现了Parcelable接口的复杂类都需要对应的.aidl文件。AIDL编译器在编译AIDL文件时会自动查找此类文件。
文件目录结构:
Person.aidl 内容如下:
package com.example.aidlservicetest;
parcelable Person;
只是声明了Person 类需要序列化和反序列化。
步骤二:创建远程服务
创建一个类继承Service类,例子里定义的是MyService类。AndroidManifest.xml文件中MyService类声明:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:process=":MyService">
<intent-filter>
<action android:name="com.example.aidlservicetest.myservice"/>
</intent-filter>
</service>
增加一个自定义action,可以让客户端使用隐式Intent绑定服务。
要让自定义的Service成为远程服务,在AndroidManifest.xml文件中为Service的声明添加android:process属性。因为Service默认的是运行在跟应用同一个进程里,如果要想Service运行在独立的进程可以添加android:process属性。该属性同样适用于其它Android组件(例如Activity, Service,Application等)。
android:process属性的写法有两种:
1)android:process=":自定义名称"
加冒号表示该进程是本应用的私有进程,当它被需要或者这个服务需要在新进程中运行的时候,这个新进程将会被创建。进程名为:本应用的标准进程名:自定义名称
例如:
本应用的标准进程名:com.example.servicetest
android:process=":myThread"
另起进程名:com.example.servicetest:myThread
2)android:process="自定义名称"
如果这个进程的名字是以小写字符开头的,则这个服务将运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。进程名为:自定义名称
步骤三:实现aidl文件定义的方法
aidl文件生成的java接口文件中已经创建了一个Stub类,Stub类继承了android.os.Binder类,实现了我们定义的接口。我们在Service中自定义一个类继承Stub类,实现aidl文件中声明的几个方法。代码如下:
class ServiceBinder extends IMyServiceInterface.Stub{
@Override
public int add(int a, int b) throws RemoteException {
return a+b;
}
@Override
public String showPerson(Person person) throws RemoteException {
return person.toString();
}
@Override
public List<Person> getPersonList(Person person) throws RemoteException {
List<Person> personList = new ArrayList<>();
personList.add(person);
return personList;
}
@Override
public Map getMap(String key, Person person) throws RemoteException {
Map map = new HashMap();
map.put(key,key);
map.put("name",person.getName());
map.put("sex",person.getSex());
return map;
}
@Override
public Person getPerson() throws RemoteException {
Person person = new Person();
person.setName("Tom");
person.setSex(0);
return person;
}
}
我们自定义的类ServiceBinder继承了Stub类,所以可以在Service的onBind(Intent intent)方法中作为参数返回。
@Override
public IBinder onBind(Intent intent) {
Log.i(Tag,"onBind");
return new ServiceBinder();
}
步骤四:绑定远程服务,调用服务的方法
在Activity中绑定远程服务Intent intent = new Intent("com.example.aidlservicetest.myservice");
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
bindService方法里面的参数serviceConnection是一个实现 ServiceConnection接口的对象,我们可以这样创建它:
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
serviceBinder = IMyServiceInterface.Stub.asInterface(iBinder);
try {
Person p = serviceBinder.getPerson();
Log.i(Tag,"getPerson:"+p.toString());
int res = serviceBinder.add(2,3);
Log.i(Tag,"add(2,3):"+res);
Person pp = new Person();
pp.setName("JOHN");
pp.setSex(0);
Map mm = serviceBinder.getMap("Key",pp);
Log.i(Tag,"getMap(\"Key\",pp):"+mm);
String str = serviceBinder.showPerson(pp);
Log.i(Tag,"showPerson:"+str);
List<Person> list = serviceBinder.getPersonList(pp);
Log.i(Tag,"getPersonList:"+list);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(Tag,"onServiceDisconnected:unbind");
}
};
当Service绑定之后会回调 onServiceConnected 方法,传入的参数有一个IBinder 对象,通过IMyAidlInterface.Stub.asInterface(iBinder); 可以获取远程服务的Binder对象。有了这个Binder对象就可以调用远程服务的方法了。
输出的结果为:
转载注明出处:http://blog.csdn.net/u012861467/article/details/51684484
源码地址
服务器端:https://github.com/StarsAaron/AIDLServiceTest.git
客户端:https://github.com/StarsAaron/AIDLClientTest.git
这是两个不同的应用,在服务器端也可以绑定远程服务,,当服务器端和客户端同时绑定了远程服务,这时候有两个Activity绑定了远程服务,当其中的一个解绑了,远程服务还是没有销毁的,直到两个绑定都解绑了才会调用onDestroy销毁。