欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android机制系列之五 再探Binder机制(Java C++对应融会贯通)

程序员文章站 2022-04-13 11:30:27
...

系列目录 https://blog.csdn.net/jzlhll123/article/category/7671581

上一篇文章https://blog.csdn.net/jzlhll123/article/details/80516277 写的不够成熟,很多东西还不够深入。C++也遗留下来了。今日再探。断断续续也花了1-2周业余时间。


LINUX IPC机制

管道,消息队列,共享内存,信号量,Socket

Binder内存拷贝一次,性能好;安全性好check UID/PID。

查看第一部分


Java Binder

接上篇文章android机制系列之二 Binder机制, 如果不了解bindService机制和AIDL,请先展开一些基础学习。
android机制系列之五 再探Binder机制(Java C++对应融会贯通)

我们实现AIDL或者Binder的时候,几个关键点:

  1. IMyRemote implements IInterface

    添加自定义func()

  2. Stub

    MyStub extends Binder(implments IBinder) implements IMyRemote(IInterface)

    2.1 实现本地服务端func(), 实例对象写具体真实方法;

    2.2 asInterface() 给client用来得到实例对象或者代理对象;

    2.3 onTransact() 从client传递到binder驱动然后回调给server端的代码,通过parcel封装调用本地代码写回结果;

  3. Proxy

    MyProxy implment IMyRemote(IInterface)

    3.1 onServiceConnected返回的是什么?

    附录1。我们拿到的是BinderProxy。

    3.2 onServiceConnected()回到的IBinder,如果同进程queryLocalInterface不为null,就是本地实例;不然,封装成Proxy来使用;即2.2的操作。

    3.3 实现客户端func(),主要通过parcel封装通过transact() (BinderProxy JNI的方法)传递给binder驱动。

  4. Binder

    Stub的父类

    4.1 attachInterface()初始化保存mOwner和mDescriptor,用于Stub初始化;

    4.2 queryLocalInterface() 2.2 asInterface()的返回值;

    4.3 onTransact() Stub实现后,再super调一下;

    4.4 transact() 本地调用则是内部直接调用3.3 onTransact(), 远程客户端是不会使用的

  5. BinderProxy implements IBinder

    这个是真正远程客户端拿到的IBinder对象。

    5.1transact() 对应transactNative() JNI用来3.2;

    5.2queryLocalInterface() 只能return null,因为是远程客户端调用;

  6. Parcel

    parcel在这里面主要2个参数传递,一个传递跨进程数据的任务如writeString8,readCString;

    另外一种,通过JNI writeStrongBinder 和 readStrongBinder()解析获取BinderProxy对象。


C++ Binder

Bp指的是Proxy端,对应的是Client端,调用方;Bn指的是Native端,服务实现的自身。

类图:

android机制系列之五 再探Binder机制(Java C++对应融会贯通)

很多的父类基类管不过来,关注关键的几个类和关键对应java层方法。

  1. IMyRemote : IInterface

    添加func()

    相当于java层的IMyRemote 接口,而且也实现了IInterface;

  2. ProcessState&IPCThreadState

    2.1 ProcessState

    代表使用Binder的进程,进程单例;

    通过IServiceManager::addService在ServiceManager中进行服务的注册

    通过ProcessState::startThreadPool启动线程池

    通过IPCThreadState::joinThreadPool将主线程加入的Binder中。

    2.2 IPCThreadState

    代表了使用Binder的线程,这个类中封装了与Binder驱动通信的逻辑,线程单例;

    BpBinder::transact其实就是调用的这里transact()
    executeCommand回调给onTransact()

  3. BnMyRemote : BnInterface(BBinder), IMyRemote

    也有的代码直接继承BBinder

    onTransact() 从client过来的指令,这边调用真实方法func(),并写入reply;

    相当于java层的Stub的实现。

  4. BpMyRemote:BpInterface, IMyRemote

    4.1 客户端是如何拿到BpMyRemote的呢?

    java层我们知道java binder 第3节中Proxy,是通过bindService等到回调onServiceConnected()的. C++又是如何拿到的?附录2

    4.2 func()实现,通过remote(), 拿到远程Ibinder* mRemote remote()对象,则通过parcel传递参数和返回值参数transact()到binder驱动。

    相当于java层的Proxy。

  5. BpBinder implements Ibinder

    参考附录1的需要牢记的东西。类似java层BinderProxy是通过parcel返回得到的。

    参考附录2我们知道它就是客户端使用的对象。

    内部通过transact()调用Binder驱动再传达到service端。

  6. Parcel

    类似java不多解释。只是注意java层主要是java parcel和JNI。而这里的是frameworks/native/libs/binder/Parcel.cpp


总结

java & C++ Binder 对应

作用 java C++ 额外信息
MyRemote接口必须继承此项 IIterface IIterface C++提供asInterface用于转换,java靠stub自行编写
接口 IXXX IXXX
XXStub/BnXX必须继承此项 Binder BnInterface / BBinder / JavaBBinder 类似
真实本地服务代码实现接口的func并实现onTransact XXStub BnXX 关注它的onTransact()从驱动层回调
客户端拿到的远程对象 BinderProxy BpBinder 附录1,附录2 关键字parcel readStrong,onServiceConnected,interface_cast asInterface
客户端使用的时候封装的对象 XXProxy BpXXX
MyProxy同时继承MyRemote和此项 BpInterface (BpRefBase) java层没有设计BpInterface这层了但是c++的remote()其实类似java Proxy的mRemote都是拿到的BinderProxy/BpBinder
封装参数(包含Ibinder对象)传递 Parcel Parcel
IPC ProcessState / IPCThreadState 封装线程操作transact onTransact动作

引用:对应图片几张

android机制系列之五 再探Binder机制(Java C++对应融会贯通)

源码阅读心得

这里我们要牢记,查看源码的一些心得了!

  1. 看到某个func() 内部有mRemote/remote()就是客户端拿到BinderProxy/BpBinder;
  2. func() 都需要将descriptor写入Parcel JNI。如果是获取Ibinder则需要额外写入Binder token, 然后mRemote.transact()/remote->transact()就是调用binder驱动;
  3. 看到onTransact()就代表已经在server端,onTransact() switch case会从binder驱动的返回结果中parcel解析出一般数据,如果是获取IBinder就是BinderProxy/BpBinder对象;这中间就会调用真实的func(),最后写回去;
  4. 一般某个文件中会同时有func()2个同名的,要注意区分他的类作用域,一个是Stub/BnXX实体操作;一个是Proxy/BpXX代理操作。

https://blog.csdn.net/jzlhll123/article/details/80865999
回调机制会加深此处的理解。

至此,结束。


附录

附录1 onServiceConnected()拿到的IBinder是什么


bindService流程搜索onServiceConnected相关流程

client->ContextWrapper bindService

—跨进程让AMS去干活–>

ActivityManagerService bindServiceLocked -> ActiveServices requestServiceBindingLocked

—(跨进程binder调用过来的,这里其实是我们的service了)—> ApplicationThreadNative scheduleBindService->

//Proxy
public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,                                                                         
            int processState) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor); ///**
        data.writeStrongBinder(token); //***
        intent.writeToParcel(data, 0);
        data.writeInt(rebind ? 1 : 0);
        data.writeInt(processState);
        mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY); //***
        data.recycle();
    }   

先写入

 //onTransact() //Stub
 case SCHEDULE_BIND_SERVICE_TRANSACTION: {                                                                                                               
            data.enforceInterface(IApplicationThread.descriptor);
            IBinder token = data.readStrongBinder(); //***
            Intent intent = Intent.CREATOR.createFromParcel(data);
            boolean rebind = data.readInt() != 0;
            int processState = data.readInt();
            scheduleBindService(token, intent, rebind, processState);
            return true;
        }

可以继续翻阅相关code,原作者这里讲述不是很详细,readStrongBinder是拿到我们服务的descriptor走的Parcel的JNI android_os_Parcel_readStrongBinder , 然后,javaObjectForIBinder 就是构造一个gBinderProxyOffsets class,就是clazz = env->FindClass(kBinderProxyPathName); kBinderProxyPathName = “android/os/BinderProxy” .

->

IBinder binder = s.onBind(data.intent);

—>AMS publishService publishServiceLocked

—>CLIENT 再2次跨进程回去

可以看到,ActivityManagerService最后(1)处调用service的onBind()方法获取service的Stub对象.Stub是binder通信的本地端.

所以拿到的是BinderProxy对象。


附录2 C++客户端如何拿到BpMyRemote的?


由于所有的服务都addService serviceManager中,并写入descriptor和name,并写入IBinder。

客户端首先要从sp bs = defaultServiceManager()->getService(serviceName); checkService , 类似java 使用JNI Parcel,而这里frameworks/native/libs/binder/Parcel.cpp writeStrongBinder flatten_binder 拿到

IBinder *local = binder->localBinder();
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;
    obj.cookie = 0; 
} else {
    obj.type = BINDER_TYPE_BINDER;
    obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
    obj.cookie = reinterpret_cast<uintptr_t>(local);
}
//readStrongBinder()
const flat_binder_object* flat = in.readObject(false);

if (flat) {
    switch (flat->type) {
        case BINDER_TYPE_BINDER:
            *out = reinterpret_cast<IBinder*>(flat->cookie);
            return finish_unflatten_binder(NULL, *flat, in); 
        case BINDER_TYPE_HANDLE:
            *out = proc->getStrongProxyForHandle(flat->handle);
            return finish_unflatten_binder(
                static_cast<BpBinder*>(out->get()), *flat, in); 
    }    
}    

取出了BpBinder IBinder*。

sp<IServiceManager> sm = defaultServiceManager();
//这里取得就是BpBinder
sp<IBinder> binder = sm->getService(String16("media.camera"));
mCameraService = interface_cast<ICameraService>(binder);

接下来分析下,interface_cast 《T》,就是INTERFACE::asInterface(obj);

    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }     

采用了模板代码,也是类似java层,queryLocalInterface()是否得到NULL,为NULL,创建一个BpMyRemote出去。所以,这里的remote()拿到的是BpXXX。

这里跟java层类似

java是因为service,client,activityManagerService,肯定service和ams是不同进程的。即使service client是同一个进程,附录1我们知道它肯定必须是跨进程的了所以拿到的是BinderProxy。

然后client拿到以后自己去转换本地对象或者封装Proxy使用。

而C++也是Parcel里面拿到的Ibinder* / sp,然后interface_cast(类似我们aidl文件中的asInterface,而且它的内部实现就叫做asInterface)转成BpBinder或者本地实例。


附录3 基类:RefBase,所有类从此派生; 使用sp,wp引用它的子类*


sp,wp,RefBase基本知识

老罗深入讲解

理解下来,设计RefBase与weakref_impl的相互聚合设计了sp;wp则又多一个weakref_type来当做RefBase引用

1. A:RefBase

RefBase:RefBase()
mRefs(new weakref_impl(this))    
因为设计模式是,weakref_impl跟RefBase是相互有彼此的。因此会传入自己给mRefs

weakref_imp
    mStrong, mWeak 强弱引用计数

析构函数:
1. 没有sp,直接移除mRefs;
2. wp并为0,删除mRefs;
2. sp spA(new A);
sp 构造函数:
A对象指针赋值给m_ptr **直接调用A的incStrong
1. 增加弱引用计数;
2. 增加强引用计数;
3. 第一次onFirstRef

sp析构:
decStrong
1. 减少强引用计数;
2. 是否最后一个,是则释放A对象;
3. 减少弱引用(decWeak)
    3.1 减少,发现还有wp,return
    3.2 根据是否存在sp,否,删除A;是,删除mRefs
    3.3 如果wp,删除A。
wp wpA(new A);
wp 构造函数
A对象指针赋值给m_ptr 比sp多一个m_refs(接口),m_refs是由A createWeak而来
1. 增加弱引用计数;
2. wp的m_Refs得到赋值

wp析构:
就是decWeak上面减少弱引用的操作3.1~3.3
使用层面来讲就简单了。
1. 类A继承RefBase
2. 使用的时候,A* a = new A(); sp<A> spA = a; sp<A> wpA = a; sp<A> spC = new A();
3. 任何一个类,都可以实现它。当他不存在。

2018.07.16补充:
wp提升为sp,就是使用promote()得到强指针。类似java层的weakRefercence,get()以后就是一个实体类对象了。

鸣谢:

  1. Android Binder设计与实现 – 设计篇 https://blog.csdn.net/freshui/article/details/54926111

太提纲挈领,暂时不能深入理解。只提到他对linux IPC的认识。

  1. 理解 Android Binder 机制(二):C++层 https://www.2cto.com/kf/201704/635248.html

本文大部分参考它,结合手中的android6.0 5.1源码学习。

  1. Android深入浅出之Binder机制 http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html

此文通俗,以MediaService为基础学习。少量部分逻辑理不顺结合第二个帖子,和源码分析。

  1. Binder通信——用户空间C/C++层架构与Java层对接点概述 https://blog.csdn.net/a372048518/article/details/75020240

简简单单几张图。但是在学习完本帖回头来看,很有感悟。

  1. bindservice 过程 https://blog.csdn.net/xutao20170209/article/details/72677004

结合源码,辅助一些onServiceConnected流程的理解。