一个Binder的前生今世 (一):Service的创建

目前介绍binder的文章很多,但是大部分都是分层来介绍的,从驱动再到Service manager等,这些文章对研究binder的机制给了很多的帮助和教学,但是这些大部分是从系统的角度去阐述binder机制的,作为应用开发者,希望能有一个从应用开发角度去理解 binder 和其机制的介绍文章,比如从binder的生命周期角度去了解等,遂产生了这篇文章异或笔记。然后调用Parcel把这个对象写入。我们接下来就来看看,我们服务端生成了一个Service的binder后,是如何通过Parcel传递出去的。
摘要由CSDN通过智能技术生成

一个Binder的前生今世

目前介绍binder的文章很多,但是大部分都是分层来介绍的,从驱动再到Service manager等,这些文章对研究binder的机制给了很多的帮助和教学,但是这些大部分是从系统的角度去阐述binder机制的,作为应用开发者,希望能有一个从应用开发角度去理解 binder 和其机制的介绍文章,比如从binder的生命周期角度去了解等,遂产生了这系列文章的想法

Binder的历史 (字面意义的前生今世)

binder 的前身是 OpenBinder,它是一种基于对象的分布式组件框架,最早由 BeOS 公司开发,后来被 Palm 公司收购,并用于 Palm OS Cobalt 系统。OpenBinder 采用了一种类似于 COM 或 CORBA 的模型,将跨进程通信抽象为对象之间的方法调用,提供了一套完整的接口和协议来实现对象的创建、引用、继承、代理等功能。

OpenBinder 在 Palm OS Cobalt 系统中并没有得到广泛的应用,而是被 Google 公司收购,并用于 Android 系统中。Google 公司对 OpenBinder 进行了大量的修改和优化,使其更适合移动设备的特点和需求。主要的改变有以下几点:

提供了 Java 语言的绑定。
将 OpenBinder 的对象模型简化为引用计数模型,并去掉了继承、代理等复杂的功能。
将 OpenBinder 的通信协议简化为四种基本类型:数据、命令、句柄和文件描述符,并使用 Parcel 类来打包和解包数据。
将 OpenBinder 的驱动程序从用户空间移动到内核空间,并使用 mmap 和 ioctl 来进行内存映射和控制。
经过这些改变后,OpenBinder 就变成了我们现在所熟知的 binder,它成为了 Android 系统中最重要的跨进程通信机制之一。

Binder的生命周期(抽象意义的前生今世)

binder 的生命周期是指一个 binder 对象从创建到销毁的过程,它涉及到多个进程和线程之间的交互和协作。binder 的生命周期主要包括以下几个阶段:

创建:一个进程或线程可以通过继承 BBinder 类或实现 IBinder 接口来创建一个本地端 binder 对象,并通过注册到 service manager 或写入到 Parcel 中来将其传递给其他进程或线程。
获取:一个进程或线程可以通过查询 service manager 或读取 Parcel 中来获取一个远端 binder 对象的句柄,并通过继承 BpBinder 类或使用 Proxy 类来与之通信。
调用:一个进程或线程可以通过调用 transact 方法来向一个远端 binder 对象发送数据和命令,并等待其返回结果。
响应:一个进程或线程可以通过重写 onTransact 方法来接收并处理来自一个远端 binder 对象的数据和命令,并返回结果。
销毁:一个进程或线程可以通过调用 unlinkToDeath 方法来取消对一个远端 binder 对象的引用,并释放其资源。当一个远端 binder 对象没有任何引用时,它就会被销毁。

在本系列博客中,将会介绍每个阶段的具体实现和源码分析,以及一些相关的概念,例如:IBinder, IInterface, BinderProxy, Binder, BBinder,BpBinder,,Parcel,ProcessState,IPCThreadState 等以及它们的关系。希望您能够通过这系列文章对 binder 的前生今世和生命周期有一个具体的了解和概念。

Binder 应用及系统层关系图

先来上一张图:
在这里插入图片描述
上图描述了Binder在应用层的架构设计,和与C++层的具体的Binder类的关系以及与系统层(RuntimeLayer, 也可以叫AppLayer,这两个类是在一个application中共享的实例)Binder机制的关系。

Binder应用层的架构设计

先来总体介绍下: binder的总体架构主要就是C/S 架构。那么binder的C/S架构是如何设计的呢?我们接着看

Binder应用层架构设计主要采用了Proxy模式,主要分为三部分:

  1. IBinder接口,主要代表了binder实体的抽象,打个比方就类似于通话人员的对讲机,通讯双方(客户端,服务端)都需要通过这个binder实体来实现通讯。
  2. IInterface接口,主要是提供给定义具体服务接口的IMyService接口来继承的,通过它可以获取binder,也就是拿到对讲机。
  3. IMyService接口, 是一个Proxy模式的实现,具体代表的通讯双方预定好的可以具体提供哪些服务,可以理解为对讲机两端人员的暗号约定,只有约定的暗号,两端的人员才能识别,不然无法识别。它在客户端的代表就是MyService.stub.proxy,在服务端的代表就是MyService.stub.

当IMyService定义服务接口时,要继承IInterface以便具有可以获取binder实例的能力。

Binder应用层实现

好了,理解了上面的binder架构设计,我们接着来分析binder具体是如何实现上诉架构设计的。

Binder是IBinder在服务端的实现,它实现了onTranscation接口,用来接受客户端发过来的请求, 然后把请求转发给MyService.stub, MyServic.stub就是我们自己实现的处理服务。通常我们在服务端的实现方式为:

class MyService() : MyService.Stub() 

BinderProxy是IBinder在客户端的表示,它被MyService.stub.proxy持有,它实现了IBinder的transact函数,用来转发客户端MyService.stub.proxy的函数调用请求。通常我们在客服端的使用方式为:

val myService = MyService.Stub.asInterface(service);

以上就是Binder在Java应用层的基本实现和原理,就是这么简单,通则不难,理解了这些就可以在我们的应用中使用binder了。

我们了解了在应用开发中binder是怎么使用的,那么binder的底层的机制是怎样的? 为什么我们要这样用,为什么我们这样用了就可以IPC通讯了呢?

好,接下来我们就来深入分析binder底层的原理。

首先我们先来具体看下一个binder,也就是一个binder服务是如何创建出来以及如何与客户端建立起联系的。

Binder服务端的创建以及与客户端建立联系

服务端Binder的创建

先上图:
在这里插入图片描述

我们在应用层创建binder的方式一般是在服务端(server)被调用到bind方法的时候,此时,我们会创建并返回一个MyService.stub对象,然后转换成IBinder返回给bind调用。那当我们new一个MyService.stub的时候,系统是如何操作的呢,可以从上图以及开始的类关系图一探流程。

附上各个类的路径:

Binder.java : Android/frameworks/base/core/java/android/os/Binder.java

android_util_Binder : Android/frameworks/base/core/jni/android_util_Binder.cpp

JavaBBinderHolder : Android/frameworks/base/core/jni/android_util_Binder.cpp

Parcel : Android/frameworks/native/libs/binder/Parcel.cpp

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

JavaBBinder: Android/frameworks/base/core/jni/Android_util_Binder.cpp

BBinder: Android/frameworks/native/include/IBinder.h & Android/frameworks/native/libs/binder/Binder.cpp

BpBinder : Android/frameworks/native/libs/binder/BpBinder.cpp

总结: 服务端创建一个service.stub对象,这个对象继承Java层Binder,Java层的Binder在C++层持有一个JavBBinderHolder。然后返回给bind调用,bind调用返回给系统一个IBinder对象,接着系统将这个IBinder对象通过Parcel打包,然后传递此Parcel。在打包Parcel的时候会把Java层的Binder写入Parcel,写入的时候就会调用ibinderForjavaObject来生成JavaBBinder对象,JavaBBinder对象保存对Java层Binder对象的引用在mObject字段中,JavaBBiner对象继承BBinder,进而生成了BBinder对象,BBinder此时就被创建出来了,这个BBinder就是c++层最原本的Binder对象(也就是对讲机)的服务端了。

服务端Binder的传递

我们应用层通常都是通过Parcel传递binder的。上一段分析了服务端binder是如何创建的以及都包含了哪些对象,接下来我们再来看下binder是如何通过Parcel传递出去的。

首次第一步肯定是先把binder写入Parcel ,我们先来分析Java层,首先肯定是调用Java层的Parcel 写入binder,进而就会调用到Parcel的Jni层执行写入操作:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
   
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
   
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
   
            signalExceptionForError(env, clazz, err);
        }
    }
}

然后我们再看:

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
   
    if (obj == NULL) return NULL;

    // Instance of Binder?
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
   
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh->get(env, obj);
    }

    // Instance of BinderProxy?
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
   
        return getBPNativeData(env, obj)->mObject;
    }

    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

到这里就会调用到JavaBBinderHolder的get函数,对应到我们服务端Binder的创建流程,这里就会生成JavaBBinder了:

class JavaBBinderHolder
{
   
public:
    sp<JavaBBinder> get(JNIEnv* env, jobject obj)
    {
   
        AutoMutex _l(mLock);
       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值