Android进程间通讯(一)Binder介绍及AIDL的具体使用
Android进程间通讯(二)AIDL、Binder源码分析
首先我们要明白几个概念
image.png-
Binder是什么?
Binder 是IPC的通讯机制,是一种机制,实际上我们四大组件的通讯就是用的Binder -
Binder原理
为了保护进程空间不被别的进程破坏或者干扰,Linux的进程是相互独立的(进程隔离),而且一个进程空间还分为用户空间和内核(Kernel)空间,相当于把Kernel和上层的应用程序抽像的隔离开。这里有两个隔离,一个进程间是相互隔离的,二是进程内有用户和内核的隔离。
Linux进程 -
Binder框架定义了四个角色:Server,Client,ServiceManager以及Binder驱动。其中Server,Client,ServiceManager运行于用户空间,驱动运行于内核空间。这四个角色的关系类似:Server是服务器,Client是客户终端,ServiceManager是服务注册中心。
Binder是Android系统进程间通信(IPC)方式之一,但是为什么Android不使用Linux本生具有的IPC方式?
Binder优点
- 性能方面
在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。 - 安全方面
首先传统IPC的接收方无法获得对方进程可靠的UID和PID(用户ID进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。其实Binder机制主要是在本机内进行跨进程通信,而socket等IPC方式主要是用于跨网络的通信手段,因而socket不能限制了入口和出口,所以从使用场景来看,socket给用户提供的使用范围更广,而binder由于为了保证它的数据安全性,必须限制于android系统的机子中,而且在内核中就已经对UID进行了验证。
AIDL是什么?
AIDL是Android为了方便使用Binder机制出现的接口定义语言,只是Android中众多进程间通讯方式中的一种方式。其实不用这个也能达到目的,但是用这个就简化了操作。只要按照规范写一个.aidl文件,系统会帮助自动创建一个与interface同名的.java文件,里面已经帮我们自动写好一堆标准代码,这些标准化的东西,用模板自动生成即可,让开发者尽量关注功能实现上。
通俗的来讲,AIDL的作用就是,让你可以在自己的APP里绑定另一个APP的service,这样就实现了两个app间的交互,而这两个app的交互的规则具体表现就是通过.aidl文件里定义的接口里面的一些方法。
IPC(Inter-Process Communication:进程间通信。
AIDL(Android Interface definition language):Android接口定义语言。
Binder :IPC的通讯机制。
AIDL使用例子
因为是两个app间的数据交互,我这里新建了两个工程,一个代表客户端一个代表服务端,客户端和服务端都存在一个ILeoAidl接口
- 客户端和服务端公用的ILeoAidl接口和实体对象类
package com.xx.leo_service;
// Declare any non-default types here with import statements
import com.xx.leo_service.Person;
interface ILeoAidl {
void addPerson(in Person person);
List<Person> getPersonList();
}
// Person.aidl
package com.xx.leo_service;
// Declare any non-default types here with import statements
parcelable Person;
package com.xx.leo_service;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private int grade;
public Person(String name, int grade) {
this.name = name;
this.grade = grade;
}
protected Person(Parcel in) {
this.name = in.readString();
this.grade = 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(grade);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}
}
-
客户端目录结构
image.png - 主要代码
//绑定服务
private void bindService() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.xx.leo_service", "com.xx.leo_service.LeoAidlService"));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: success");
iLeoAidl = ILeoAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: success");
iLeoAidl = null;
}
};
private void initView() {
btn = (Button) findViewById(R.id.but_click);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
iLeoAidl.addPerson(new Person("小三爷", 3));
List<Person> persons = iLeoAidl.getPersonList();
Log.e(TAG, persons.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
主要就是绑定aidl服务,连接服务获取aidl对象,然后通过一个按钮,调用aidl对象的addPerson方法添加一个person,然后通过iLeoAidl接口获取list数组里的值和list的长度,并且打印日志
-
服务端目录结构
image.png
服务端主要是新建一个service,并且启动这个service就ok
- 主要代码
//清单文件里记得声明下这个服务
<service
android:name=".LeoAidlService"
android:exported="true" />
//1、新建服务
public class LeoAidlService extends Service {
private ArrayList<Person> persons;
@Nullable
@Override
public IBinder onBind(Intent intent) {
persons = new ArrayList<>();
Log.e("LeoAidlService", "success onBind");
return iBinder;
}
private IBinder iBinder = new ILeoAidl.Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override
public List<Person> getPersonList() throws RemoteException {
return persons;
}
};
@Override
public void onCreate() {
super.onCreate();
Log.e("LeoAidlService", "onCreate: success");
}
}
//2、启动服务
startService(new Intent(this, LeoAidlService.class));
image.png
好,我们现在根据代码再分析下流程
1、服务端和客户端都定义一个aidl接口,接口里有增加和显示list的方法
2、客户端点击按钮,调用aidl的接口方法,往list里面增加数组,然后日志打印数据
结论
运行代码,第一次先把客户端和服务端app都运行起来,
- 现象一:只要服务端的app没杀死(客户端app随便杀死不杀死),每次点击客户端增加按钮list数组里的person都会+1.
- 现象二:如果把服务端端杀死了(客户端会连接会中断onServiceDisconnected),这时再运行客户端再运行起来,list数组数量就会从0开始了。
这两种现象说明,客户端的数据确实不是存在客户端的,而是通过aidl接口调用到服务端代码里的方法,来增加和显示数据的,其实这些数据都是服务端的数据,只是客户端获得了控制服务端数据操作的“遥控器”
网友评论