什么是AIDL
AIDL 是 Android Interface Definition Language(Android 接口定义语言)的缩写,它是 Android 进程间通信的接口语言。由于 Android 系统的内核是 Linux,它采用了进程隔离机制,使得不同的应用程序运行在不同的进程当中,有时候两个应用之间需要传递或者共享某些数据,就需要进行进程间的通信讯。
Android 进程间通信的方式有很多种,比如 Messenger、文件(SharePreference)、AIDL、Socket 和 Content Provider 等,它们当中 Messenge、AIDL 和 Content Provider 的底层都是依赖于 Binder 机制去实现的。除此之外,Android 四大组件的启动和通信的核心过程也是通过 Binder 机制去实现的,这里,我们借助 AIDL 来了解 Binder 的实现机制。
Linux IPC 原理
由于 Linux 采用了虚拟地址空间技术,操作系统在逻辑上将虚拟内存分为用户空间(User Space)和内核空间(Kernel Space),普通应用程序运行在用户空间,系统内核运行在内核空间,为了控制应用程序的访问范围、保证系统安全,用户空间只能通过系统调用的方式去访问内核空间。当进程执行系统调用而陷入内核代码的时候,该进程则进入了内核态,相比之下,进程在用户空间执行自己的代码的时候,则是处于用户态。
由于进程 A 和进程 B 的虚拟地址不同,因此它们之间是相互透明的,都以为自己独享了系统的资源,当然也不能直接跟对方交互。但是,有些情况下有些进程难免会需要跟其他进程进行交互,这个交互过程就叫 IPC(Inter-Process Communication,进程间通信)。IPC 的实质就是数据的交互,因此我们这里将进行 IPC 过程中的通信调用方和被调用放分别称为数据发送方和数据接收方
IPC 通信的过程如下: 1. 数据发送方进程将数据放在内存缓存区,通过系统调用陷入内核态 2. 内核程序在内核空间开辟一块内核缓存区,通过 copy_from_user 函数将数据从数据发送方用户空间的内存缓存区拷贝到内核空间的内核缓存区中 3. 数据接收方进程在自己的用户空间开辟一块内存缓存区 4. 内核程序将内核缓存区中通过 copy_to_user 函数将数据拷贝到数据接收方进程的内存缓存区
通过以上过程,一次 IPC 就完成了,但是这种传统的 IPC 机制有两个问题:
- 性能比较低:整个过程数据的传递需要经历发送方内存缓存区——内核缓存区——接收方内存缓存区的过程
- 浪费空间: 接收方进程事先不知道需要开辟多大的内存用于存放数据,因此需要开辟尽可能大的空间或者事先调用 API 来解决这个问题,这两种方式不是浪费空间就是浪费时间。
Binder IPC 原理
为了克服 Linux 传统的 IPC 机制中的不足之处,Android 系统引入了 Binder 机制,从字面上看 Binder 是胶水的意思,在这里,Binder 的职责是在不同的进程之间扮演一个桥梁的角色,让它们之间能够相互通信。从上一小节内容可以了解到,进程间的通讯少不了 Linux 内核的支持,而 Binder 并不属于内核的一部分,但是,得益于 Linux 的 LKM(Loadable Kernel Module) 机制:
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行 因此,Binder 作为这种模块存在于内核之中,也称为 Binder 驱动。回顾上一小节的内容,传统 Linux IPC 的过程需要经历两次数据拷贝,Binder 借助 Linux 的另一个特性,只用一次数据拷贝,就能实现 IPC 过程,这就是内存映射:
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。
内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
Binder IPC 通信过程如下:
- Binder 驱动在内核空间创建一个数据接收缓存区
- 然后在内核空间开辟一块内存缓存区并与数据接收缓存区建立映射关系,同时,建立数据接收缓存区与数据接收方的内存缓存区的映射关系
- 数据发送方通过系统调用 copy_from_user 函数将数据从内存缓存区拷贝到内核缓存区,由于内核缓存区通过数据接收缓存区跟数据接收方的内存缓存区存在间接的映射关系,相当于将数据直接拷贝到了接收方的用户空间,这样便完成了一次 IPC 的过程。
用binder的优势:
- 1.拷贝只需要一次,虽然有不需要拷贝的通信机制,但是就会涉及到信息资源时刻同步,这个是很耗性能的,而且控制极其复杂,易用性也差
- 2.安全,因为为每个app分配UID(可以看作是应用的身份证)。
AIDL的理解参考文章
借助 AIDL 理解 Android Binder 机制
上层Binder原理解析 (讲的非常好)
下层binder原理解析(讲的非常好 但是太难太难了)
Android Bander设计与实现 - 设计篇
正常service绑定 是service 通过sericeManger( 服务查找绑定
AIDL实现跨进程通信的实例
1、创建服务端的AIDL文件
// PersonController.aidl
package com.xm.studyproject.android.aidl;
// Declare any non-default types here with import statements
//导入实体类地址
import com.xm.studyproject.android.aidl.User;
interface UserController {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
// in:输入类型参数
// out:输出类型参数
// inout:输入输出类型参数
boolean addUser(in User user);
List<User> getUsers();
}
2、创建服务端实体类
3、创建服务端实体类对应的AIDL文件
4、AIDL实现类的生成
这块只要你创建了AIDL文件 就会自动在这个位置生成
5、同理 新建项目为客户端 创建相同的上面一套 (保证2个项目包名一致)
你也可以跟我一样在同一个项目中创建,只需要将其中一个项目的设置为远程进程即可
6、在服务端自定义一个远程进程的Service
7、客户端发起Service连接
8、绑定成功后的回调
至此通过AIDL实现跨进程通信的实例算是完结了
特别说明:
这例子看起来好像是客户端通过AIDL往服务端的Service发起请求,请求成功后返回连接,实际上是服务端优先通过ServiceManager(这也是一个服务 而且是独立的 所以也需要binder通信,不过这个地方看源码,只是单纯的一个final类 不太理解)进行Service的注册,而客户端也是通过ServiceManager先是找到真正的服务 就是AMS 找到注册好了的service的代理类(不会把真实的Service返回回去) 然后再进行通信.
核心难点:AIDL通信的源码分析
1、讲解AIDL类结构
分为UserController、内部类Stub以及Stub内部类Proxy
拿UserController文件讲解,但是个人感觉只能说通客户端往服务端发送数据、等待服务端返回数据的流程,但是还不能解释服务端针对客户端如何绑定的,以及服务端一开始是怎么处理的,后续内容继续分析
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.xm.studyproject.android.aidl;
/**
* 继承系统的IInterface接口
*/
public interface UserController extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
//内部类Stub
public static abstract class Stub extends android.os.Binder implements com.xm.studyproject.android.aidl.UserController {
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getUsers = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
private static final java.lang.String DESCRIPTOR = "com.xm.studyproject.android.aidl"
+ ".UserController";
/**
* Construct the stub at attach it to the interface.
*/
//初始化 DESCRIPTOR是接口的唯一标识符
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.xm.studyproject.android.aidl.UserController interface,
* generating a proxy if needed.
*/
//通过服务端返回的Binder 来生成真实的代理类
public static com.xm.studyproject.android.aidl.UserController asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//优先从本地获取AIDL的实现接口
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//如果本地有这个接口 表明 这2个AIDL文件是在同一个进程内 那就不需要跨进程通信
if (((iin != null) && (iin instanceof com.xm.studyproject.android.aidl.UserController))) {
return ((com.xm.studyproject.android.aidl.UserController) iin);
}
//如果不在同一个进程内 那就生成代理类 代理类是Stub的内部类 接下来去看Proxy类结构
return new com.xm.studyproject.android.aidl.UserController.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
//接收
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
//如果这个是客户端的话 那就是服务端调用了 transact(Stub.TRANSACTION_addUser, _data, _reply, 0);方法过来的
// 把数据携带过来
case TRANSACTION_addUser: {
//校验AIDL
data.enforceInterface(descriptor);
com.xm.studyproject.android.aidl.User _arg0;
if ((0 != data.readInt())) {
//读参数
_arg0 = com.xm.studyproject.android.aidl.User.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//添加成功标识符
boolean _result = this.addUser(_arg0);
//读数据返回
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
case TRANSACTION_getUsers: {
data.enforceInterface(descriptor);
java.util.List<com.xm.studyproject.android.aidl.User> _result = this.getUsers();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.xm.studyproject.android.aidl.UserController {
private android.os.IBinder mRemote;
//这个是服务端传过来的Binder
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public boolean addUser(com.xm.studyproject.android.aidl.User user) throws android.os.RemoteException {
//_data 是发送数据 _reply是接收数据
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
//写入标识符 为了去ServiceManager找到对应的Service进行校验、判断
_data.writeInterfaceToken(DESCRIPTOR);
if ((user != null)) {
_data.writeInt(1);
//写入数据
user.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//往真实的Service发送请求 这个会把这个线程挂起来 直到服务端返回数据
//Stub.TRANSACTION_addUser 算是方法的标示 方法是通过int值标示 更省性能
//_data 发送数据v 哈哈哈哈哈哈哈哈哈哈
//_reply 接收数据
//flag 为0 表示客户端可以发送到服务端 服务端也可以返回 为1表示客户端可以发送到服务端 但是服务端不可以返回
//最终调用的是Binder的onTransact(code, data, reply, flags);方法
//发送
mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
_reply.readException();
//读完数据返回结果
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.util.List<com.xm.studyproject.android.aidl.User> getUsers() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.xm.studyproject.android.aidl.User> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getUsers, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.xm.studyproject.android.aidl.User.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public boolean addUser(com.xm.studyproject.android.aidl.User user) throws android.os.RemoteException;
public java.util.List<com.xm.studyproject.android.aidl.User> getUsers() throws android.os.RemoteException;
}
通过binderService方法如何绑定的
1、bindService(intent, connection, Context.BIND_AUTO_CREATE);
mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);讲解
ActivityManager.getService()讲解
ServiceManager.getService(Context.ACTIVITY_SERVICE)
上面注解意思是 返回对具有给定名称的服务的引用。 * * @param名称要获取的服务的名称* @返回对该服务的引用;如果服务不存在,则返回,那我们这里会返回ActivityManagerService
IActivityManager am = IActivityManager.Stub.asInterface(b);是什么呢
回到
注意上面是28版本 但是我实在是找不到IActivityManager类 只能使用SDK23
因此 ActivityManagerNative.getDefault().bindService(xxx)会走到
这步会走到activtiyManagerService的bindService 不是很懂
bindService()
ContextImpl#bindServiceCommon()
ActivityManagerNative.getDefault().bindService
ActivityManagerNative.getDefault()--》Proxy.bindService
IActivityManager==UserController aidl 接口
ActivityManagerNative == Stub
ActivityManagerProxy == Proxy
bindService(app, token, service, resolvedType, conn, fl,
callingPackage, userId);===Service
ActivityManagerService === Service
ActivityManagerService#bindServiceLocked()
1.ActiveServices#bringUpServiceLocked()
2.ActiveServices#requestServiceBindingLocked(s, b.intent, callerFg, true);
1
// APP 已经创建了
app != null && app.thread != null
realStartServiceLocked(r, app, execInFg);
app.thread.scheduleCreateService()
//创建服务的方法
handleCreateService()
service = (Service) cl.loadClass(data.info.name).newInstance();--->LeoAidlService
//APP没有创建
if (app == null)
2
//绑定与否
handleBindService();
IBinder binder = s.onBind(data.intent);===>iBinder
ActivityManagerNative.getDefault().publishService()===>AMS.publishService()
c.conn.connected(r.name, service);===>Client=>onServiceConnected()
IServiceConnection conn;
ConnectionRecord c
connection.onServiceConnected()
IServiceConnection===ServiceDispatcher.InnerConnection
7.A进程访问B进程时的几种状态
- 1.进程B未启动
- 2.进程B启动了,但是Service没有创建
- 3.进程B启动了,Service创建了,但是Service没有绑定过,回调onBind()
- 4.进程B启动了,Service创建了,但是Service绑定过,回调onRebind()
常见面试题
核心方法:mRotate.transact(Stub.TRANSACTION_xxx,_data,_reply,flags);
1、Binder有什么优势?(字节跳动真题)
2、Binder是如何做到一次拷贝的?(腾讯真题)
3、MMAP的原理讲解;(腾讯真题)
mmap() 是一个系统调用函数,本质是一种进程虚拟内存的映射方法,可以将一个文件、一段物理内存或者其它对象映射到进程的虚拟内存地址空间。 实现这样的映射关系后,进程就可以采用指针的方式来读写操作这一段内存,进而完成对文件的操作,而不必再调用read/write 等系统调用函数了
4、描述AIDL生成的Java类细节;(字节跳动真题)
aidl实例
5、四大组件底层的通信机制;(字节跳动真题)
6、为什么Intent不能传递大数据?(阿里真题)(这个只是临时备注)