参考学习视频:2021字节跳动、腾讯、阿里巴巴、美团、OPPO、华为、百度等一线
目录
Binder是什么?(C/S)
Binder就是Android中的血管。在Android中我们所使用的Activity,Service等组件都需要和AMS(system_server)通信,这种跨进程的通信都是通过Binder完成。
- 机制:Binder是一种进程间通信机制;
- 驱动:Binder是一个虚拟物理设备驱动;
- 应用层:Binder是一个能发起通信的Java类;
多进程的使用及优势:
虚拟机分配给各个进程的运行内存是有限制的,LMK(Low Memory Killer,进程回收机制)也会优先回收对系统资源的占用多的进程。
- 突破进程内存限制:如图库占用内存过多;
- 功能稳定:独立的通信进程保存长连接的稳定性;
- 规避系统内存泄露:独立的WebView进程阻隔内存泄露导致的问题;
- 隔离风险:对于不稳定的功能放入独立进程,避免导致主进程崩溃;
查看每个虚拟机分配的内存大小命令:
adb shell
getprop dalvik.vm.heapsize
常用设置多进程的方式:
在AndoridManifest.xml文件的配置中添加
android:process
问题一:Binder有什么优势?(字节跳动)
Linux进程间通信机制有:管道、socket、信号、消息队列、共享内存等等。
内存划分:
内存被操作系统划分成两块:用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。
用户空间和内核空间其实所指的都是虚拟内存,它们只是一个地址,用户操作的时候,只能操作这个虚拟地址,但是这个虚拟地址会映射到一个物理内存,也就是说,用户操作虚拟地址,其实就是操作物理地址中的数据。
虚拟地址是操作系统进行管理的,虚拟地址映射到物理地址真正最终处理是由硬件管理的。
所有应用的内核内存空间是共享的。
物理内存其实就是:内存条、磁盘、硬盘等
从进程1向进程2发送消息是通过上图中的绿色箭头进行传输。
以前传统通信方式:
binder通信方式:
Binder与传统IPC对比
Binder | 共享内存 | Socket | |
---|---|---|---|
性能 | 需要拷贝一次 | 无需拷贝 | 需要拷贝两次 |
特点 | 基于C/S架构,易用性高 | 控制复杂,易用性差 | 基于C/S架构,作为一款通用接口,其传输效率低,开销大 |
安全性 | 为每个APP分配UID,同时支持实名和匿名 | 依赖上层协议,访问接入点是开放的,不安全 | 依赖上层协议,访问接入点是开放的,不安全 |
拷贝次数是指系统调用次数,也就是cpu调度次数。
从用户空间拷贝到内核空间:copy_from_user,从内核空间拷贝到用户空间:copy_to_user
如何区分实名服务还是匿名服务:看它有没有注册到SM(ServiceManager,相当于一个服务大管家)中。
SystemServiceManager:管理服务的生命周期
ServiceManager:管理binder服务的,handle = 0
SystemServer:是一个进程,包含90+多个服务,管理SystemService,SystemService代表是系统服务
AMS是实名服务
自己创建的服务是匿名服务
问题二:Binder是如何做到一次拷贝?(腾讯)
接收方和内核有一块共享区域。
binder传输数据
发送方将数据拷贝到内核地址空间后,内核地址空间是一个虚拟内存,它是没有放数据,最终是将数据放到物理内存中,而接收方和内核空间有一块共享的物理内存。binder其实就是相对于将接收方用户空间与发送方的内核空间其中的有块物理内存进行了共享。发送方发送数据,就是把数据放到了共享区域中,这样就等同于放到了接收方的用户空间中。
问题三:MMAP的原理讲解(腾讯)
Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping, mmap)。
对文件进行mmap,会在进程的虚拟内存分配地址空间,创建映射关系。
实现这样的映射关系后,就可以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的文件磁盘上。
所有的系统资源管理都是在内核空间中完成的。比如读写磁盘文件,分配回收内存,从网络接口读写数据等等。用户空间通过系统调用让内核空间完成这些功能。
写文件流程:
- 调用write,告诉内核需要写入数据的开始地址与长度;
- 内核将数据拷贝到内核缓存
- 由操作系统调用,将数据拷贝到磁盘,完成写入
问题四:Binder机制是如何跨进程的(阿里)
由于发送方进程1和接收方进程2不能直接进行通信,由于内核空间是共享的,发送方通过copy_from_user()把数据直接拷贝到内核空间,因为内核空间与接收方的用户空间同时映射到一块物理内存中,所以说数据通过copy_from_user()拷贝到内核地址空间指定的虚拟内存后,相对于直接进入了接收方的用户空间,所以整个通信过程只进行了一次内存拷贝,这个映射就是通过mmap实现的。
问题五:描述AIDL生成的java类细节(字节跳动)
Proxy类是提供给客户端使用
,Stub类是提供给服务端使用
IPersonManager.java
public interface IPersonManager extends IInterface {
String DESCRIPTOR = "com.example.binder.common.IPersonManager";
void addPerson(Person person) throws RemoteException;
List<Person> getPersonList() throws RemoteException;
void setData(byte[] data) throws RemoteException;
}
Person.java
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 +
'}';
}
}
Proxy.java
// 客户端使用
public class Proxy implements IPersonManager {
private IBinder mRemote;
public Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public void addPerson(Person person) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
data.writeInt(1);
person.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
Log.e("abc", "Proxy,addPerson: " + Thread.currentThread());
mRemote.transact(Stub.TRANSACTION_addPerson, data, reply, 0);
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
@Override
public List<Person> getPersonList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List<Person> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersonList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Person.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void setData(byte[] data) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeByteArray(data);
// 同步:0 异步:IBinder.FLAG_ONEWAY
mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
Stub.java
// 服务端使用
public abstract class Stub extends Binder implements IPersonManager {
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IPersonManager asInterface(IBinder binder) {
if ((binder == null)) {
return null;
}
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if ((iin != null) && (iin instanceof IPersonManager)) {
return (IPersonManager) iin;
}
return new Proxy(binder);
}
@Override
public IBinder asBinder() {
return this;
}
// 第三步,进入服务端的onTransact()方法
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_addPerson:
Log.e("abc", "Stub,TRANSACTION_addPerson: " + Thread.currentThread());
data.enforceInterface(DESCRIPTOR);
Person arg0 = null;
if ((0 != data.readInt())) {
arg0 = Person.CREATOR.createFromParcel(data);
}
this.addPerson(arg0); //第四步
reply.writeNoException();
return true;
case TRANSACTION_getPersonList:
data.enforceInterface(DESCRIPTOR);
List<Person> result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSACTION_setData:
data.enforceInterface(DESCRIPTOR);
byte[] _arg0;
_arg0 = data.createByteArray();
this.setData(_arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;
static final int TRANSACTION_setData = IBinder.FIRST_CALL_TRANSACTION + 2;
}
ClientActivity.java
public class ClientActivity extends AppCompatActivity {
private IPersonManager iPersonManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, RemoteService.class);
intent.setAction("com.example.binder");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Log.e("abc", "------------onClick:" + Thread.currentThread());
iPersonManager.addPerson(new Person("abc", 3)); // 第二步:调用接口
List<Person> persons = iPersonManager.getPersonList();
Log.e("abc", persons.toString() + "," + Thread.currentThread());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
iPersonManager.setData(new byte[1 * 1024 * 1024 - 4096 * 2]);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("abc", "onServiceConnected: success");
iPersonManager = Stub.asInterface(service);// proxy 第一步:获得代理对象
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("abc", "onServiceDisconnected: success");
iPersonManager = null;
}
};
}
RemoteService.java
public class RemoteService extends Service {
private ArrayList<Person> persons;
@Nullable
@Override
public IBinder onBind(Intent intent) {
persons = new ArrayList<>();
Log.e("abc", "success onBind");
return iBinder;
}
private IBinder iBinder = new Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override
public List<Person> getPersonList() throws RemoteException {
return persons;
}
@Override
public void setData(byte[] data) throws RemoteException {
Log.e("abc", "数据发送成功" );
}
};
}
IBinder对象
通过Stub.asInterface(service)获取IBinder对象,执行asInterface最后会返回new Proxy(binder)一个代理对象。
第一步:先获得代理对象iPersonManager = Stub.asInterface(service);
Stub中的asInterface方法就是区分是跨进程还是非跨进程,如果是跨进程就方法代理对象,如果不是跨进程就直接返回服务端
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iPersonManage = Stub.asInterface(service); //如果是跨进程,iPersonManage就是代理对象
}
@Override
public void onServiceDisconnected(ComponentName name) {
iPersonManager = null;
}
}
stub.java
public static IPersonManager asInterface(IBinder binder) {
if (bind == null) {
return null;
}
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if ((iin != null && (iin instanceof IPersonManager)) {
return (IPersonManager) iin; //非跨进程
}
return new Proxy(binder); // 跨进程
}
客户端:
第二步:调用接口 iPersonManager.addPerson(new Person("abc", 3));
其实这个地方调用到Proxy类的addPerson()方法,但这个代理对象没有实现具体的功能,主要功能是进入Binder。
在这个方法中,
- 首先会创建两个包data和reply
data包:指客户端准备发给服务端的数据
reply包:指服务端准备返回什么数据 - 执行mRemote.transact()方法
mRemote.transact—>mRemote指的就是IBinder,执行这条语句之后,客户端就会挂起,等待服务器去完成它的处理。
客户端是否会挂起,这个需要分两种情况进行处理(一般去通信的话都是同步情况):同步情况
,客户端会挂起,如果在主线程执行,可能会出现ANR异步情况
,客户端不会挂起
Proxy.java
下面的接口主要是进入binder
@Override
public void addPerson(Person person) throws RemoteException {
parcel data = Parcel.obtain(); //data包:指客户端准备发给服务端的数据
Parcel reply = Parcel.obtain(); //reply包:指服务端准备返回什么数据
try {
data.writeInterfaceToken(DESCRIPTOR);
if (person != null) {
data.writeInt(1);
person.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, data, reply,0); //mRemote.transact()流程其实就是客户端-->binder-->服务端,最后一个参数flags = 0:表示同步逻辑操作,如果改成oneway表示异步操作
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
第三步:进入服务端Stub的onTransact()方法,找到对应的方法
服务端(Stub):
服务端会实现onTransact()方法,通过IBinder的方法id来判断客户端调用服务端的哪个方法。
Stub.java
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_addPerson:
data.enforceInterface(DESCRIPTOR); //解包
Person arg0 = null;
if ((0 != data.readInt())) {
arg0 = Person.CREATOR.createFromParcel(data);
}
this.addPerson(arg0); //调用到服务端中对应的接口方法
reply.writeNoException();
return true;
case TRANSACTION_getPersonList:
data.enforceInterface(DESCRIPTOR);
List<Person> result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result); //如果有返回结果,会将返回结果写到reply中
return true;
case TRANSACTION_setData:
data.enforceInterface(DESCRIPTOR);
byte[] _arg0;
_arg0 = data.createByteArray();
this.setData(_arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;
static final int TRANSACTIOn_setData = IBinder.FIRST_CALL_TRANSACTIOn + 2;
第四步:通过执行this.addPerson(arg0)会调用到服务端的addPerson()方法。
问题六:为什么Intent不能传递大数据(阿里)
限制大小: 1M - 8K
实际传递过程中比(1M - 8K)还要小些,数据还需要打包
就好像网络通信过程中数据会有包头、命令等
ProcessState.cpp
ProcessState.cpp
如果在异步情况下,限制大小: (1M - 8K)/ 2
binder.c
proc->free_async_space = proc->buffer_size / 2;
native层,什么类代表binder? --> BBinder、JavaBBinder
java层,什么类代表binder?–> Binder
什么数据结构代表binder? --> binder_node结构体
补充:
AMS服务注册流程:
ServiceManagerProxy、BinderProxy、BpBinder、binder_ref是ServiceManager在不同层级的代理对象
Binder、JavaBBinder(继承BBinder)是ServiceManager在不同层级的实体对象
应用获取AMS服务流程:
整个流程
特别注意:如果服务端是AMS,则BinderProxy、BpBinder为AMS的代理对象