AIDL(Android Interface Definition Language),Android接口定义语言。一般利用AIDL来进行进程间通信,大概流程如下:
1、服务端进程创建一个Service,供客户端进程来连接,通过创建AIDL文件定义好接口,然后Android Studio会自动帮我们生成对应AIDL的.java文件,然后服务端实现接口里的方法,并在onBind方法中返回自定义Binder对象,Binder相当于是服务端和客户端通信的桥梁。
2、客户端需要把服务端的AIDL文件复制到客户端app的工程下,切记包名也要一样,如果在调用AIDL的方法中使用到了自定义的类,也一样需要把该类的.java文件一并复制过来,同样,包名也必须一致,有了服务端的AIDL文件,我们只需要绑定服务端的Service,在绑定成功后,拿到服务端onBind方法返回的Binder对象进行一下转换,就能调用服务端实现的方法了。这样就实现了两个进程间的通信。
接下来看具体实现(为了方便,在同一个app中让Service运行在另外一个进程中,然后通过主进程去和该Service通信,这和两个app之间的通信实质上是一样的,只是省去了复制AIDL文件和自定义类文件了):
这里使用Android Studio来编写,右键->新建一个名为IPersonService的AIDL,这时IDE会自动帮我们在main包下建立一个aidl的包,里面放着刚才IPersonService.aidl文件:
// IPersonService.aidl
package com.example.androidipc;
// Declare any non-default types here with import statements
import com.example.androidipc.Person;
interface IPersonService {
List<Person> getPerson();
void addPerson(in Person p);
}
IPersonService.aidl中声明了两个方法,getPerson获取所有person list,addPerson往集合中添加一个person对象,在addPerson方法的参数前面有一个in,是说明我们这个参数是属于输入型的。再看看Person.aidl:
// Person.aidl
package com.example.androidipc;
parcelable Person;
Person对应的.java文件:
package com.example.androidipc;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Rainbow556 on 11/22/2015.
*/
public class Person implements Parcelable{
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
protected Person(Parcel in) {
name=in.readString();
age=in.readInt();
}
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 dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
@Override
public String toString() {
return name+" , "+age;
}
public String getName() {
return name;
}
}
上一篇博客说过,支持跨进程传输的类型只有基本数据类型和Android中特定的某些类,如果要传输自定义类的话,这个类必须要实现Parcelable接口。
下面是工程结构:
有一点要注意,就算IPersonService和Person位于同一个包下,但在IPersonService中还是要显示的导入Person类,否则会提示找不到类。
接着看Service的代码:
public class PersonService extends Service {
private List<Person> persons=new ArrayList<>();
public PersonService() {
}
@Override
public void onCreate() {
super.onCreate();
Person p=null;
for (int i=0;i<3;i++){
p=new Person("lx"+i,18+i);
persons.add(p);
}
}
@Override
public IBinder onBind(Intent intent) {
return new PersonBinder();
}
public class PersonBinder extends IPersonService.Stub{
@Override
public List<Person> getPerson() throws RemoteException {
return persons;
}
@Override
public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// String packageName = null;
// //拿到调用者的包名,可以做权限验证
// String[] packages = PersonService.this.getPackageManager().
// getPackagesForUid(getCallingUid());
// if (packages != null && packages.length > 0) {
// packageName = packages[0];
// }
// if (!ALLOW_PACKAGE.equals(packageName)) {
// return false;
// }
return super.onTransact(code, data, reply, flags);
}
}
}
可以看到在Service中自定义个了PersonBinder继承了IPersonService.Stub,并重写了三个方法,注意一下onTransact方法,在这个方中可以对调用者进行自定义的权限验证,如果返回false,则调用者会调用失败。
再看activity中绑定Service,调用aidl中的方法:
public class AIDLActivity extends Activity {
IPersonService mService;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
ServiceConnection mServiceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IPersonService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent i=new Intent(this,PersonService.class);
bindService(i,mServiceConnection,BIND_AUTO_CREATE);
tv = (TextView) findViewById(R.id.tv);
}
public void getPersonList(View v){
try {
tv.setText(mService.getPerson().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void addPerson(View v){
Person p=new Person("lj",22);
try {
mService.addPerson(p);
tv.setText(mService.getPerson().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
在activity中建立一个ServiceConnection,去绑定服务,在绑定成功后调用IPersonService.Stub.asInterface方法把传递过来的IBinder转换成IPersonService对象,这样就可以调用服务端aidl中的方法了,大概流程就是这样。