前言:Binder 是什么?从类的角度来说,它是实现了 IBinder 接口的 Binder 类;从机制角度来说,它是 Android 的 IPC(进程间通信) 机制。
官方文档:
https://developer.android.google.cn/reference/android/os/Binder
注:本文以 API 30 的 Binder 源码进行解析。
Binder 与传统 IPC 对比:
Binder | 共享内存 | Socket | 管道 | |
---|---|---|---|---|
性能 | 需要拷贝一次 | 无需拷贝 | 需要拷贝两次 | 需要拷贝两次 |
特点 | 基于 C/S 架构,易用性高 | 控制复杂,易用性差 | 基于 C/S 架构,易用性差 | 易用性差 |
安全性 | 为每个 APP 分配 UID,同时支持实名和匿名 | 依赖上层协议,访问接入点是开放的,不安全 | 依赖上层协议,访问接入点是开放的,不安全 | 依赖上层协议,访问接入点是开放的,不安全 |
了解 Binder 源码之前首先来了解一下 AIDL,因为 APP 主要是通过 AIDL 与 Binder 机制进行通信。
一、AIDL 源码解析
写一个 AIDL 接口IMyAidlInterface.aidl
:
interface IMyAidlInterface {
void testAidl();
}
Rebuild Project,就会在build
文件夹下看到 Android Studio 为我们自动生成的代码:
// AIDL 继承自 IInterface,也就是说所有的 AIDL 接口都是 IInterface 的组成类。
public interface IMyAidlInterface extends android.os.IInterface {
...
}
看一下这个自动生成的 AIDL 的组成结构:
首先看Stub
:
public static abstract class Stub extends android.os.Binder implements com.tyhoowu.myapplication.IMyAidlInterface
根据源码可知 Stub 是一个抽象类,这也就是为什么我们在写服务端的时候需要实现 Stub 类。Stub 继承自 Binder,实现我们自定义的 AIDL 接口。
再来看Proxy
:
private static class Proxy implements com.tyhoowu.myapplication.IMyAidlInterface
根据源码可知 Proxy 直接就实现了我们自定义的 AIDL 接口。
问:两个进程要进行通信的话,Stub 和 Proxy 哪个是负责发送,哪个负责是接收?
答:Proxy 向 Binder 发送数据,Stub 接收 Binder 发送过来的数据。
从客户端发送到服务端,再从服务端返回的流程
1.定义唯一的类名:
private static final java.lang.String DESCRIPTOR = "com.tyhoowu.myapplication.IMyAidlInterface";
2.通过attachInterface
把Stub
和DESCRIPTOR
传进去:
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
3.在attachInterface
里,把Stub
和DESCRIPTOR
进行保存:
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
4.asInterface
:
public static com.tyhoowu.myapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
// 判断 IBinder 是否为 null
if ((obj == null)) {
return null;
}
// 调用 queryLocalInterface
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
// 当前在同一个进程
if (((iin != null) && (iin instanceof com.tyhoowu.myapplication.IMyAidlInterface))) {
return ((com.tyhoowu.myapplication.IMyAidlInterface) iin);
}
// 服务端在本地保存 descriptor,如果客户端和服务端不在一个进程的话,那么返回的 iin 就为空,那么此时就会 new 一个 Proxy。
return new com.tyhoowu.myapplication.IMyAidlInterface.Stub.Proxy(obj);
}
5.queryLocalInterface
:
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
// 将传入的 descriptor 和 mDescriptor 进行比较,
// 因为两个应用有可能在同一个进程里面(自己调用自己的服务)
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
6.Proxy
:
private android.os.IBinder mRemote;
// 将传入的 remote 保存到 IBinder
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
7.testAidl
@Override
public void testAidl() throws android.os.RemoteException {
// 通过 “池” obtain 创建了 _data 和 _reply
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
// 校验
_data.writeInterfaceToken(DESCRIPTOR);
// 发送到 Binder,然后通知给服务端
// 参数1:int 型,因为通信传 String 的话会导致包过大,影响性能,客户端传一个数,服务端就知道这个数对应的方法。
// 参数2:要处理的数据
// 参数3:接收服务端返回给客户端的数据
// 参数4:标志位,如果是0,就表示服务端能返回数据,如果是1,就表示服务端不能返回数据。
boolean _status = mRemote.transact(Stub.TRANSACTION_testAidl, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().testAidl();
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
8.通过Binder
的一系列处理就会调用到onTransact
:
@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) 找到对应的方法
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_testAidl: {
// 进行校验
data.enforceInterface(descriptor);
this.testAidl();
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
二、Binder 源码解析
创建服务的一种做法是调用bindService
,以bindService
流程为例来走进Binder
。
bindService
是Context
的抽象方法:
public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags);
根据 Android 基础,Context 下面有两个类:
- ContextWrapper
- ContextImpl(Context 的实现类)
所以实际上就是调用ContextImpl
的bindService
:
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null, getUser());
}
ContextImpl
的bindServiceCommon
:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
// 这块实际上就是 ServiceConnection,
// 然后会回调 ServiceConnection 的 onServiceConnected,
// 通过 onServiceConnected,进程与服务就进行了绑定。
IServiceConnection sd;
...
try {
...
// 阅读源码 ActivityManager.getService() 实际上返回的就是 Proxy
// 阅读源码 bindIsolatedService 实际上就是调用 Proxy
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
...
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
A进程访问B进程时的状态:
- 进程B有没有启动
- 进程B启动了,但是里面的Service没创建出来
- 进程B启动了,里面的Service也创建了,但是Service没有被绑定过,回调onBind()
- 进程B启动了,里面的Service也创建了,但是Service已经被绑定过,回调onRebind()
总结
AIDL 接口 | Stub 抽象类 | Proxy 类 | Stub 的实现类 |
---|---|---|---|
IMyAidlInterface | Stub | Proxy | new IMyAidlInterface.Stub |
IActivityManager | ActivityManagerNative | ActivityManagerProxy | ActivityManagerService |
© 2020 Tyhoo Wu, All rights reserved.
网友评论