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

news2025/1/12 18:06:10

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

  • 一个Binder的前生今世
    • Binder的历史 (字面意义的前生今世)
    • Binder的生命周期(抽象意义的前生今世)
    • Binder 应用及系统层关系图
    • Binder应用层的架构设计
    • Binder应用层实现
    • Binder的创建
      • 服务端Binder的创建
      • 服务端Binder的传递
      • Binder在客户端的传递
      • 客户端BinderProxy的创建
    • 小结

一个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, Interface, 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的方式一般是在服务端bind的时候,此时,我们会返回一个MyService.stub对象,然后转换成IBinder返回给binder调用。那当我们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。
然后在Parcel传递的时候,会把Java层的Binder写入Parcel,写入的时候就会调用ibinderForjavaObject来生成JavaBBinder对象,JavaBBinder对象保存对Java层Binder对象的引用在mObject字段中,JavaBBiner对象继承BBinder,进而生成了BBinder对象,BBinder此时就被创建出来了,这个BBinder就是最根本的Binder对讲机的服务端了。

服务端Binder的传递

我们应用层一般时候binder都是通过Parcel传递的。我们接下来就来看看,我们服务端生成了一个Service的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1016966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Trino HTTPS 与密码认证介绍与实战操作

文章目录 一、概述二、安装 Trino三、配置 HTTPS1&#xff09;生成证书2&#xff09;配置 Trino3&#xff09;修改 Trino docker-compose yaml 文件4&#xff09;开始部署 Trino5&#xff09;测试验证 四、密码认证1&#xff09;开启密码认证2&#xff09;创建密码认证配置文件…

AndroidStudio 安装与配置【安装教程】

1.下载软件 进入官网https://developer.android.google.cn/studio&#xff0c;直接点击下载 2.阅读并同意协议书 直接下滑至最底部 如果这里出现了无法访问 官方地址&#xff1a;https://redirector.gvt1.com/edgedl/android/studio/install/2022.3.1.19/android-studio-2022.…

java:杨辉三角形

public class YangHui {public static void main(String[] args){int yangHui[][] new int[10][];for (int i 0; i < yangHui.length;i){yangHui[i] new int[i 1];for (int j 0; j < yangHui[i].length; j){ // 最初和最后的数值都是1if (j 0 || j …

LeetCode 847. Shortest Path Visiting All Nodes【状态压缩,BFS;动态规划,最短路】2200

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

深度解析shell脚本的命令的原理之pwd

pwd是Print Working Directory的缩写&#xff0c;是一个Unix和Linux shell命令&#xff0c;用于打印当前工作目录的绝对路径。以下是对这个命令的深度解析&#xff1a; 获取当前工作目录&#xff1a;pwd命令通过调用操作系统提供的getcwd&#xff08;或相应的&#xff09;系统调…

带自动采集小说网站源码 小说听书网站源码 小说网站源码 带教程

PTCMS可听书可下载的小说站源码 带自动采集和搭建视频教程 必装环境&#xff1a;Nginx(apache.iis也可)&#xff0c;mysql,php5.6,memcached php5.6安装扩展memcache新建站点&#xff0c;注意新建时&#xff0c;PHP版本必须选择PHP5.6 安装教程 1.上传网站文件到网站目录&…

看完这篇 教你玩转渗透测试靶机Vulnhub——Grotesque:1.0.1

Vulnhub靶机Grotesque&#xff1a;1.0.1渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;漏洞利用GetShell&#xff1a;③&#xff1a;Keepass文件的解密&#xff1a;④&…

科技抗老新突破,香港美容仪品牌内地重磅上市

近年来&#xff0c;新消费时代“颜值经济”的火热促使美容行业市场规模增长迅速&#xff0c;越来越多的人愿意为“美”买单&#xff0c;对美的需求也随之增长&#xff0c;美容行业已经成为成长最快的新锐产业。随着经济和科技的发展&#xff0c;“快捷”也成为了当今社会的时代…

【网络】计算机网络基础

Linux网络 对网络的理解 在网络传输中存在的问题&#xff1a; 找到我们所需要传输的主机解决远距离数据传输丢失的问题怎么进行数据转发&#xff0c;路径选择的问题 有问题&#xff0c;就有解决方案&#xff1b; 我们把相同性质的问题放在一起&#xff0c;做出解决方案 解…

【C++】深拷贝和浅拷贝 ③ ( 浅拷贝内存分析 )

文章目录 一、浅拷贝内存分析1、要分析的代码2、调用有参构造函数创建 Student 实例对象3、调用默认拷贝构造函数为新对象赋值4、修改拷贝对象成员变量指针指向的数据5、析构报错 一、浅拷贝内存分析 1、要分析的代码 下面的代码中 , 没有定义拷贝构造函数 , 因此 C 编译器会自…

无涯教程-JavaScript - CSC函数

描述 CSC函数返回以弧度指定的Angular的余割值。 语法 CSC (number)争论 Argument描述Required/OptionalNumberThe angle (in radians) that you want to calculate the cosecant of.Required Notes CSC(n)等于1/SIN(n) 如果Angular为度,则将其乘以PI()/180或使用RADIANS…

每日一题 198. 打家劫舍

难度&#xff1a;中等 这是昨天的每日一题忘记做了&#xff0c;没想到正好是今天的题目的简化版&#xff0c;详细思路可以看http://t.csdn.cn/Smr0t 代码&#xff1a; class Solution:def rob(self, nums: List[int]) -> int:if len(nums) 1:return nums[0]a, b nums[0…

怒刷LeetCode的第3天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;动态规划 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;模拟 方法二&#xff1a;数学规律 方法三&#xff1a;分组 第三题 题目来源 题目内容 解决方法 方法一&#xff1a;数学方法 方法…

001-项目介绍

项目介绍 文章目录 项目介绍编写目的小小说明 项目介绍目前状态目录项目介绍第一代第二代第三代软件部署硬件篇 总结 关键字&#xff1a; Qt、 Qml、 分享、 记录、 目录 编写目的 这是我目前参与的一个真实项目&#xff0c;而且我非常幸运地能够从头到尾地参与其中。在面…

微信小程序|自定义弹窗组件

目录 引言小程序的流行和重要性自定义弹出组件作为提升用户体验和界面交互的有效方式什么是自定义弹出组件自定义弹出组件的概念弹出层组件在小程序中的作用和优势为什么需要自定义弹出组件现有的标准弹窗组件的局限性自定义弹出组件在解决这些问题上的优势最佳实践和注意事项避…

Shell脚本编写:从零到精通

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

C#-WinForm-发送邮件

登录QQ邮箱——设置——开启“POP3/SMTP服务” 登陆QQ邮箱→打开设置→开启“POP3/SMTP服务”&#xff0c;获取“授权码” 简单总结一下&#xff1a; 1、使用SmtpClient发送电子邮件是很简单的&#xff0c;只要正确创建了MailMessage对象和SmtpClient就可以很容易的发送出去电…

Mybatis-Plus入门(1)

单表的CRUD功能代码重复度很高&#xff0c;也没有什么难度。而这部分代码量往往比较大&#xff0c;开发起来比较费时。因此&#xff0c;目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是MybatisPlus. 官方网站如下&#xff1a; 简…

超级好用绘图工具(Draw.io+Github)

超级好用绘图工具&#xff08;Draw.ioGithub&#xff09; 方案简介 绘图工具&#xff1a;Draw.io 存储方式&#xff1a; Github 1 Draw.io 1.2 简介 ​ 是一款免费开源的在线流程图绘制软件&#xff0c;可以用于创建流程图、组织结构图、网络图、UML图等各种类型的图表。…

【面试题】forEach能跳出循环吗?

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 【国庆头像】- 国庆爱国 程序员头像&#xff01;总有一款适合你&#xff01; 如果面试官&#xff0c;或者有人问你foreach怎么跳出循环&#xff0c;请你…