从AIDL来看Binder的通信原理(基于Andorid8

通过 startService()bindService()两种方式开启,在framwork框架层中没有发现流程中使用add_service方法(甚至都没出现ServiceManager)。

↑ 这就看出无论自定义Service最后在不在ServiceManager中,Android本身是不希望我们通过ServiceManager的途径去获取使用自定义Service的。

上述第二条的结论,原因是什么?我不是很清楚,但是基于结论来看:

只要一个进程持有另一进程的BinderProxy类,那我还去找 ServiceManager干什么,这不更加省事吗?

所以,在自定义的流程中把ServiceManager忽略掉,我们就不用考虑Service到底有没有注册在ServiceManager中了。

[](()2. 从AIDL上看Binder

==================================================================================

AIDL本质上是在Java层对Binder进行了封装。所以Android8.0前后AMS是否使用了AIDL,本质都是使用Binder + 代理模式,只是说使用了 AIDL通过编译时生成代码,节省了一些代码的编写,提高了开发效率。

那么我们就从编译时产生的代码开始,来看看Binder的运作机制,这里使用的例子是:[使用AIDL来进行进程间通信]((),就是一个 Service远程服务 + User类 + IUserManager代理类

[](()2.1 用于进程传输的Stub类


在编译文件产生的 IUserManager中,它是一个接口类,同时它有一个静态内部类 Stub

// IUserManager.java

// 0

public static abstract class Stub extends android.os.Binder implements com.rikkatheworld.aidl_demo.IUserManager {

private static final java.lang.String DESCRIPTOR = “com.rikkatheworld.aidl_demo.IUserManager”;

// 1

public Stub() {

this.attachInterface(this, DESCRIPTOR);

}

// 2

public static com.rikkatheworld.aidl_demo.IUserManager asInterface(android.os.IBinder obj) {

}

// 3

@Override

public android.os.IBinder asBinder() {

return this;

}

// 4

@Override

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {

}

// 5

private static class Proxy implements com.rikkatheworld.aidl_demo.IUserManager {

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {

mRemote = remote;

}

// 6

@Override

public com.rikkatheworld.aidl_demo.User getUser(java.lang.String name) throws android.os.RemoteException {

}

}

}

Stub类分成两个部分,一个是自己本身的成员方法,另一个是Proxy类,先来看看这些成员。

(1)自身部分

  1. 注释0

Stub类继承自 Binder,说明这个类是用于进程间传输的。

  1. 注释1 Stub()

构造方法,把自身和标记自身的descriptor传入,后者用于表明身份

  1. 注释2 asInterface(obj)

静态方法, 处理ActivityManagerService丢给我们的 IBinder,将它转换成 客户端进程想要的类。

  1. 注释3: asBinder()

用于Binder传输的过程,需要转换自身的类型,Stub(A进程)->IBinder(Binder)->Stub(B进程),就用到这个方法,这个例子非常粗糙,可能也有问题,但是这个方法是辅助用的,我们不需要对这个方法深入理解。

  1. 注释4 :onTransact()

重写父类的 onTrancsact,这个方法是客户端进程在使用Binder进行数据传输时需要用到的数据。

(2)Proxy代理类

  1. 注释5

静态内部类,实现了 IUserManager接口

内部定义了一个 IBinder类型的 mRemote对象

  1. 注释6

实现方法,如果客户端进程要跨进程调用Server端的方法,就要通过调用这些方法。

我们看看客户端进程是怎么使用这个类的:

private ServiceConnection mServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

// 1

IUserManager userManager = IUserManager.Stub.asInterface(service);

try {

User user = userManager.getUser(“Rikka”);

} catch (RemoteException e) {

e.printStackTrace();

}

}

@Override

public void onServiceDisconnected(ComponentName name) {

}

};

AIDL的客户端进程,在 onServiceConnected()中拿到 AMS传过来的 service,通过 asInterface()转化成 IUserManager,然后调用它的方法,就能完成跨进程的方法调用了。

我们来看看 asInterface()具体实现:

// IUserManager.java

public static com.rikkatheworld.aidl_demo.IUserManager asInterface(android.os.IBinder obj) {

if ((obj == null)) {

return null;

}

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

if (((iin != null) && (iin instanceof com.rikkatheworld.aidl_demo.IUserManager))) {

return ((com.rikkatheworld.aidl_demo.IUserManager) iin);

}

return new com.rikkatheworld.aidl_demo.IUserManager.Stub.Proxy(obj);

}

可以看出调用了 queryLocalInterface()获得一个 IInterface类型的对象iin,如果iin不为空,则返回iin,如果为空,则 new一个 Proxy类并返回。

我们知道AMS给我们返回的一定是Binder的对象,所以我们去 Binder中看看queryLocalInterface()

// Binder.java

public class Binder implements IBinder {

// 1

public @Nullable IInterface queryLocalInterface(@NonNull String descrip Android开源项目:ali1024.coding.net/public/P7/Android/git tor) {

if (mDescriptor != null && mDescriptor.equals(descriptor)) {

return mOwner;

}

return null;

}

}

// 2

final class BinderProxy implements IBinder {

// 3

public IInterface queryLocalInterface(String descriptor) {

return null;

}

}

可以发现Binder文件中,有两处实现了这个方法,在Binder类中。

注释1中 通过 descriptor标识来返回,mOwner是Bidner本身。

注释2、3是 BinderProxy实现的,直接返回一个空。

在Stub类的构造方法里我们就看到了只要Stub被构建就会调用:

public Stub() {

this.attachInterface(this, DESCRIPTOR);

}

按理来说,queryLocalInterface不可能返回为空,我们应该都是得到一个正宗的 Stub类才对,但是实际上这里出现了偏出。

在跨进程服务中,我们通过 asInterface() 返回的是一个 IUserManager.Stub.Proxy的对象。这就说明 queryLocalInterface()返回了空,只有一种情况会导致这种结果, 那就是 ServiceConnection返回来的值是一个 BinderProxy类型的对象。

这就是我最开始的疑问点,为什么 onServiceConnection()中远端进程和同进程下返回的是Binder,而不同进程下返回的是BinderProxy

[](()2.2 BindService 客户端进程传出流程


Binder机制是C/S架构,任意两个进程,请求方是C端,接收方是S端

C端通过 BpBinder来访问S端,S端通过 BBinder来访问C端,而BpProxy就是BpBinder的封装类。他们的具体知识这里就不再赘述了。详情请看皇叔的文章。下面以框架层角度画个图来看下 客户端进程的数据传出经过了什么方法:

在这里插入图片描述

万物的开端在于BindService,在 [Service的startService和bindService源码流程](()中,我们知道主进程的最终代码是到 ContextImpl.bindServiceCommon()

// ContextImpl.java

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler

handler, UserHandle user) {

IServiceConnection sd;

if (mPackageInfo != null) {

sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);

} else {

throw new RuntimeException(“Not supported in system context”);

}

try {

IBinder token = getActivityToken();

int res = ActivityManager.getService().bindService(

mMainThread.getApplicationThread(), getActivityToken(), service,

service.resolveTypeIfNeeded(getContentResolver()),

sd, flags, getOpPackageName(), user.getIdentifier());

} …

}

// ActivityManager

public static IActivityManager getService() {

return IActivityManagerSingleton.get();

}

private static final Singleton IActivityManagerSingleton =

new Singleton() {

@Override

protected IActivityManager create() {

final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

final IActivityManager am = IActivityManager.Stub.asInterface(b);

return am;

}

};

ActivityManager.getService()是 IActivityManager.Stub.asInterface(b),这个肯定是Proxy类(套娃了,不过问题不大),我们要来看看bindService做了什么,由于IActivityManager是编译产生的,所以我暂时还没有找到IActivityManager,但是我们知道 它的实现肯定和我们自己所编译出来的如出一辙,甚至我们自己都可以写出来:

// 这个地方是照着 IUserManger.Stub.Proxy 仿写的,也看了开篇Blog的内容

// IActivityManager.Stub.Proxy

@Override

public int bindService(IApplicationThread caller, IBinder token,

Intent service, String resolvedType, IServiceConnection connection, int flags, int userId) throws android.os.RemoteException {

android.os.Parcel _data = android.os.Parcel.obtain(); // 1

android.os.Parcel _reply = android.os.Parcel.obtain(); // 2

int _result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder(caller != null ? caller.asBinder() : null); // 3

_data.writeStrongBinder(token); // 4

service.writeToParcel(data, 0);

_data.writeString(resolvedType);

_data.writeStrongBinder(connection.asBinder()); // 5

_data.writeInt(flags);

_data.writeInt(userId);

mRemote.transact(TRANSACTION_bindService, data, reply, 0); // 6

reply.readException();

_result = reply.readInt(); // 7

} finally {

_reply.recycle();

_data.recycle();

}

return _result;

}

注释1:获得一个传出去的序列化数据 data

注释2:获得一个用于接收的序列化数据 reply

注释3、4、5: 通过 writeStrongBinder()把 Binder数据写入到 data中

注释6:调用原生层的 transact(),传入数据

注释7:在返回结果reply读出数据result

其中,注释3、4、5、6是这个方法的关键。就是参数是这么从客户端进程传出去的。

我们来看看 writeStrongBinder(),它是一个native方法:

// frameworks/base/core/jni/android_os_Parcel.cpp

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)

{

Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);

if (parcel ! 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源 = NULL) {

const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));

if (err != NO_ERROR) {

signalExceptionForError(env, clazz, err);

}

}

}

这里调用了两个函数, 一个是调用了 ibindForJavaObject得到一个IBinder,在传入到 Parcel->writeStrongBinder()中:

先来看看前者

// frameworks/base/core/jni/android_os_Parcel.cpp

sp ibinderForJavaObject(JNIEnv* env, jobject obj)

{

if (obj == NULL) return NULL;

if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {

JavaBBinderHolder* jbh = (JavaBBinderHolder*)

env->GetLongField(obj, gBinderOffsets.mObject);

return jbh != NULL ? jbh->get(env, obj) : NULL; // 1

}

if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {

return (IBinder*)

env->GetLongField(obj, gBinderProxyOffsets.mObject); // 2

}

return NULL;

}

注释1:如果传进来的binder是 gBinderOffsets,则 new一个 JavaBBinder并返回。

注释2:如果传进来的bind是 gBinderProxyOffsets(代理类),类中的 代理对象。(也就是封装在里面了,取出来用)

由于我们是客户端进程,所以进来的并不是代理类,所以 ibinderForJavaObject()会返回一个 JavaBBinder返回给我们。

再来看看 Parcel.writeStrongBinder():

// frameworks\native\libs\binder\Parcel.cpp

status_t Parcel::writeStrongBinder(const sp& val)

{

return flatten_binder(ProcessState::self(), val, this);

}

status_t flatten_binder(const sp& /proc/,

const sp& binder, Parcel* out)

{

if (binder != NULL) {

IBinder *local = binder->localBinder(); // 1

if (!local) {

BpBinder *proxy = binder->remoteBinder();

if (proxy == NULL) {

ALOGE(“null proxy”);

}

const int32_t handle = proxy ? proxy->handle() : 0;

obj.type = BINDER_TYPE_HANDLE;

obj.binder = 0; /* Don’t pass uninitialized stack data to a remote process */

obj.handle = handle;

最后

一线互联网Android面试题含详解(初级到高级专题)

这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率

如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。

最后送给大家一句话,望共勉,永远不要放弃自己的梦想和追求;
NULL) {

IBinder *local = binder->localBinder(); // 1

if (!local) {

BpBinder *proxy = binder->remoteBinder();

if (proxy == NULL) {

ALOGE(“null proxy”);

}

const int32_t handle = proxy ? proxy->handle() : 0;

obj.type = BINDER_TYPE_HANDLE;

obj.binder = 0; /* Don’t pass uninitialized stack data to a remote process */

obj.handle = handle;

最后

一线互联网Android面试题含详解(初级到高级专题)

这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率

[外链图片转存中…(img-5CnY23p5-1649944953211)]

如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。

最后送给大家一句话,望共勉,永远不要放弃自己的梦想和追求;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值