一、AIDL进程间通信简介
AIDL可以理解为进程间通信的中介;
1、客户端和服务端需要创建相同的AIDL接口文件,如下所示,定义了一个名为BookController的aidl接口文件,另外定义了一个名为Book的aidl实体文件;
// BookController.aidl
package com.sunnyweather.android;
import com.sunnyweather.android.Book;
// Declare any non-default types here with import statements
interface BookController {
List<Book> getBookList();
void addBookInOut(inout Book book);
}
// Book.aidl
package com.sunnyweather.android;
// Declare any non-default types here with import statements
parcelable Book;
2、我们需要在客户端和服务端针对实体aidl进行实现,并且包名及类名要一致,如下所示,其实就是实现Parcelable接口,因为在进程间通信需要对象可序列化;
package com.sunnyweather.android;
public class Book implements Parcelable {
private String name;
public Book(String name) {
this.name = name;
}
protected Book(Parcel in) {
name = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@NonNull
@Override
public String toString() {
return "book name: " + name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
}
public void readFromParcel(Parcel dest){
name = dest.readString();
}
}
3、以上两步是客户端和服务端进行的同样的操作,下面针对客户端和服务端分别处理;
服务端的处理代码如下,注释1处表示当客户端通过bindService绑定服务端时,服务端会通过onBind方法将IBinder对象返回给客户端,这里返回的对象类型就是AIDL接口文件自动生成的java文件的内部类Stub对象,这个Stud继承了Binder并且实现了步骤1中的BookController接口,因此可以作为IBinder类型对象返回;
注释2处表示我们在服务端Stud的匿名内部类实现了此接口中的两个方法,这样在客户端调用此接口中的方法时就会回调到这里;
public class AIDLService extends Service {
private List<Book> bookList;
public AIDLService() {
}
@Override
public IBinder onBind(Intent intent) { //1
return stub;
}
@Override
public void onCreate() {
super.onCreate();
bookList = new ArrayList<>();
Book book = new Book("算法导论");
bookList.add(book);
}
private final BookController.Stub stub = new BookController.Stub() { //2
@Override
public List<Book> getBookList() {
return bookList;
}
@Override
public void addBookInOut(Book book) {
if (book != null){
book.setName("服务器改了新书的名字 InOut");
bookList.add(book);
}
}
};
}
4、客户端的处理代码如下,注释4处的bindService表示客户端与服务端进行绑定,绑定成功会回调到注释1处,我们在注释1处将服务端返回的IBinder对象通过BookController.Stub.asInterface(iBinder)方法获得BookController接口对象,然后在注释2和注释3处调用接口中的两个方法,就会回调到服务端中匿名内部类Stub的内部实现中去;
public class MainActivity extends AppCompatActivity {
private final String TAG = "ClientMainActivity";
private BookController bookController;
private boolean connected;
private List<Book> bookList;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
bookController = BookController.Stub.asInterface(iBinder); //1
connected = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
connected = false;
}
};
private final View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_getBookList:
if (connected){
try {
bookList = bookController.getBookList(); //2
} catch (RemoteException e) {
e.printStackTrace();
}
log();
}
break;
case R.id.btn_addBook_inout:
if (connected){
Book book = new Book("这是一本新书");
try {
bookController.addBookInOut(book); //3
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
};
private void log() {
for (Book book: bookList){
Log.e(TAG, book.toString());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_getBookList).setOnClickListener(clickListener);
findViewById(R.id.btn_addBook_inout).setOnClickListener(clickListener);
bindServices(); //4
}
private void bindServices() {
Intent intent = new Intent();
try {
intent.setPackage("com.sunnyweather.android");
intent.setAction("com.sunnyweather.android.action");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (connected){
unbindService(serviceConnection);
}
}
}
二、进程间通信
1、方式介绍:
进程间通信的方式有共享内存、Binder、Socket、管道、信号量等,各种方式特点如下:
共享内存:数据无需拷贝;但控制复杂,易用性差;访问接入点是开放的,不安全;
Binder:数据拷贝一次;基于C/S架构,易用性好;为每个APP分配UID,同时支持实名和匿名,安全性好;
Socket等其他方式:数据拷贝两次;效率低,开销大;访问接入点是开放的,不安全;
综上所述,Android进程间通信选用的是Binder;
2、内存划分:
内存被操作系统划分为两块,用户空间和内核空间,他们之间是相互隔离的,用户空间专门存放用户程序代码运行的地方,内核空间是内核代码运行的地方;这样即使用户程序崩溃了内核也不会受影响;
32位操作系统的寻址空间为2^32,即总共可访问地址为4G;内核为1G,用户空间为3G;
64位操作系统,低位0-47位才是有效的可变地址(256T),高位48-63位全补0对应的寻址空间是用户空间,高位48-63位全补1对应的寻址空间是内核空间;
3、传统IPC传输数据:
传统IPC在进程之间传输数据时需要拷贝两次;内核空间首先调用copy_from_user()将数据从用户空间拷贝到内核缓存区(第一次拷贝),然后内核空间调用copy_to_user()将数据从内核缓存区拷贝到用户空间(第二次拷贝);
4、Binder传输数据:
Binder在进程之间传输数据时仅需要拷贝一次;首先内核空间通过copy_from_user()将数据拷贝到一块物理内存区域,然后内核空间将自己的一块虚拟空间指向该物理内存,最后接收方用户进程通过mmap()将其虚拟内存指向该物理内存,便可以直接使用该物理内存中的数据了,这样也就实现了仅需一次拷贝;
mmap:Linux通过将一个虚拟内存区域与一个物理内存区域关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping),简称mmap;对文件进行mmap,会在进程的虚拟内存分配地址空间,创建映射关系,实现这样的映射关系后,就可以通过指针的方法读写操作这一段内存,而系统会自动回写到对应的文件磁盘上;
三、Binder框架
1、简介
Binder框架可分为Client端和Server端,根据层级又会分为FrameWork层,JNI层,Native层和Kernel驱动层;
FrameWork层在客户端和服务端的代表是BinderProxy和Binder;Native层在客户端和服务端的代表是BpBinder和BBinder;
2、Binder内核驱动层
在Binder驱动层,主要会调用四个方法:
binder_init 创建dev/binder设备节点
binder_open 获取Binder Drive的文件描述符
binder_mmap 在内核分配一块内存,用于存放数据
binder_ioctl 将IPC数据作为参数传递给Binder Drive
binder_ioctl_write_read 这个方法是binder_ioctl比较重要的方法,在数据读写的时候经常调用
binder_thread_write 进行写操作时调用的方法
binder_thread_read 进行读操作时调用的方法
3、传输的数据在内核层的大小限制为4M,应用层限制为1M-8K;
四、service_manager
1、启动service_manager
启动service_manager进程通过service_manager.c的main方法,主要有三步:
binder_open 打开binder驱动,申请128k字节大小的内存空间
binder_become_context_manager 设为守护进程,称为binder的管理者
binder_loop 进入无限循环,处理client端发来的请求
2、获取service_manager
获取service_manager主要通过defaultServiceManager方法,主要有四步:
ProcessState::self
ProcessState::getContextObject
Interface_cast
BpServiceManager
3、addService流程
addService主要通过以下三步流程执行:
SystemServer.run
getIServiceManager
SMP.addService
用命令总结(同步状态下):首先CLient发送BC_TRANSACTION命令给Binder驱动;接着Binder驱动发送BR_TRANSACTION_COMPLETE命令给客户端(客户端进入休眠等待状态),发送BR_TRANSACTION命令给Server端,Server端从休眠中被唤醒执行操作;然后执行完操作之后,发送BC_REPLY命令给Binder驱动;最后Binder驱动发送BR_TRANSACTION_COMPLETE命令给Server端(服务端进入休眠等待状态),发送BR_REPLY命令给客户端,将客户端从休眠等待中唤醒;
BC代表Binder Command;BR代表Binder Return;