精华内容
下载资源
问答
  • binder机制
    2021-12-02 16:59:15

    一 Android与Linux通信机制的比较

    虽然Android继承使用Linux的内核,但Linux与Android的通信机制不同。
    在Linux中使用的IPC通信机制如下:
    1.管道(Pipe):点对点通信,因为采用存储转发方式,需要拷贝2次数据,效率低下
    2.Socket:是一个通用的接口,通常用于网络请求,但是建立连接和断开连接开销太大,效率低下
    3.共享内存:虽然在传输时没有拷贝数据,但其控制机制复杂(比如跨进程通信时,需获取对方进程的pid,得多种机制协同操作)。因为内存共享,数据很容易被一方篡改,所以安全性不高

    android的binder通信:
    性能方面
    在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
    安全方面
    首先传统IPC的接收方无法获得对方进程可靠的UID和PID(用户ID进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。其实Binder机制主要是在本机内进行跨进程通信,而socket等IPC方式主要是用于跨网络的通信手段,因而socket不能限制了入口和出口,所以从使用场景来看,socket给用户提供的使用范围更广,而binder由于为了保证它的数据安全性,必须限制于android系统的机子中,而且在内核中就已经对UID进行了验证。

    二 Binder机制原理

    2.1 虚拟内存

    我们知道以前程序直接访问和操作的都是物理内存;这样做有很大弊端,首先不能进行多进程运行,试想一下:你必须执行完一条指令后才能接着执行下一条,如果是多进程的话,由于直接操作物理内存地址,当一个进程给内存地址1000赋值后,另一个进程也同样给内存地址赋值,那么第二个进程对内存的赋值会覆盖第一个进程所赋的值,这回造成两条进程同时崩溃。这时候就抽象一个概念叫虚拟内存:每个进程都有自己独立的逻辑地址空间,然后这个空间以“页”为单位,将连续的地址空间分开,对于进程来看,逻辑上貌似有很多内存空间,其中一部分对应物理内存上的一块(称为页框,通常页和页框大小相等),还有一些没加载在内存中的对应在硬盘上。那么虚拟内存和物理内存如何实现映射呢?是通过一个叫MMU(内存管理单元),MMU里面主要负责两件事:1.记录该页是否在物理内存中运行 2.记录该页空间在物理内存哪里运行 。如果虚拟内存的页并不存在于物理内存中,会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。
    这样通过引入虚拟空间的概念,就能让物理内存充分的利用,同时利用各个进程逻辑地址分页运行的特性,实现多进程运行也不是难事

    2.2 用户空间和内核空间

    我们上面已经知道了虚拟内存的由来;每个进程可以拥有4GB的虚拟内存。Linux内核将这4GB的空间分为两个部分。最高的1GB(从虚拟地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间“。而较低的3GB(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间”,因为每个进程可以通过系统调用进入内核,因此,Linux内核空间由系统内的所有进程共享。所以,每个进程可以拥有4GB的虚拟地址空间(也叫虚拟内存)。每个进程有各自的私有用户空间(0~3GB),这个空间对系统中的其他进程是不可见的。最高的1GB内核空间则为所有进程以及内核所共享(虽然共享,但是一个完整的虚拟地址空间就是4GB,内核空间是每个进程都要被分配的,只是他们对应相同的地址空间)。
    在这里插入图片描述

    2.3 原理

    2.3.1 隔离

    为了保护进程空间不被别的进程破坏或者干扰,Linux的进程是相互独立的(进程隔离),而且一个进程空间还分为用户空间和内核(Kernel)空间,相当于把Kernel和上层的应用程序抽像的隔离开。这里有两个隔离,一个进程间是相互隔离的,二是进程内有用户和内核的隔离。

    2.3.2 组成

    Binder框架定义了四个角色:Server,Client,ServiceManager以及Binder驱动。其中Server,Client,ServiceManager运行于用户空间,驱动运行于内核空间。这四个角色的关系类似:Server是服务器,Client是客户终端,ServiceManager是服务注册中心(类似房屋中介)。
    在这里插入图片描述
    要进行Client-Server之间的通信,从面向对象的角度,在Server内部有一个Binder实体,在Client内部有一个Binder对象的引用,其实就是Binder的一个代理,Client通过对Binder引用间接的操作Server内部的Binder实体,这样就实现了通信。

    但是现在的问题是会有很多个提供不同服务的Server(比如有媒体播放服务,音视频捕获服务等),而且会有很多Client(比如多个应用都要调用媒体播放服务),那么我们怎么才能够实现正确的Client调用正确的Server呢?就好比房客怎么才能够租到自己想要租的房子(联系上房东),这个时候中介就起到重要作用,房东想要出租自己的房子就必须要到中介注册,房客想要租房子就要去中介那里找,同样的道理,这里Client就是房客,Server就是房东,ServiceManager就是房屋中介,每个Server如果要提供服务就必须要去ServiceManager那里去注册,ServiceManager在一张查找表中记录一个Server的名字,对应着Server的引用。Client想要获得Server,必须通过名字到ServiceManager取找Server的引用,获得这个Server的binder引用,通过这个binder引用去和Server通信。

    2.3.3 宏观分析

    在这里插入图片描述

    Binder 驱动:
    Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
    可以说是Client,Server,ServerManager之间沟通的桥梁

    ServiceManager 与实名 Binder(Server)**
    ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址以外还有自己的网址。Server创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。细心的读者可能会发现,ServierManager 是一个进程,Server 是另一个进程,Server 向ServiceManager 中注册 Binder 必然涉及到进程间通信。当前实现进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋!Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋。ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 实体,其他进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用 BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)。其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个0 号引用和 ServiceManager 的 Binder 通信。类比互联网,0 号引用就好比是域名服务器的地址,你必须预先动态或者手工配置好。要注意的是,这里说的 Client 是相对于ServiceManager 而言的,一个进程或者应用程序可能是提供服务的 Server,但对于ServiceManager 来说它仍然是个 Client。

    Client 获得实名 Binder 的引用
    Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用了。Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder: 我申请访问名字叫张三的 Binder 引用。ServiceManager 收到这个请求后从请求数据包中取出 Binder名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为回复发送给发起请求的Client。从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样

    2.3.4 从内存角度分析

    • 进程空间划分:

    上面说过。进程之间的内存划分为各自进程的用户空间和内核空间;用户空间指的是用户程序所运行的空间,内核空间是 Linux 内核的运行空间,为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

    • 系统调用:

    用户态和内核态虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现

    • 系统调用是用户空间访问内核空间的唯一方式
      保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用。当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。系统调用主要通过如下两个函数来实现:copy_from_user() //将数据从用户空间拷贝到内核空间copy_to_user() //将数据从内核空间拷贝到用户空间2.2 Linux下的传统IPC通信原理
    copy_from_user() //将数据从用户空间拷贝到内核空间
    copy_to_user() //将数据从内核空间拷贝到用户空间
    

    Linux下的传统IPC通信原理

    在这里插入图片描述

    通常的做法是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copyfromuser() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copytouser() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信。这种传统的 IPC 通信方式有两个问题:性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

    Binder跨进程通信原理动态内核可加载模块 && 内存映射这个动态内核可加载模块,就是Binder驱动,运行在内核空间,使各个用户进程通过Binder实现通信。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。Binder涉及到的内存映射通过mmap()实现

    Binder实现原理
    在这里插入图片描述
    首先Binder驱动在内核空间创建一个数据接收缓存区;接着在内核空间开辟一块内存缓存区,建立内核缓冲区和内核中数据接收缓存缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系发送方通过系统调用copy from user()将数据copy到内核中的内核缓冲区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间。

    三 Binder通信中的代理模式

    A 进程想要 B 进程中某个对象(object)是如何实现的呢?它们分属不同的进程,A 进程没法直接使用B 进程中的 object。

    前面我们介绍过跨进程通信的过程都有 Binder 驱动的参与,因此在数据流经 Binder 驱动的时候驱动会对数据做一层转换。当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了。
    在这里插入图片描述

    更多相关内容
  • Binder做为Android中核心机制,对于理解Android系统是必不可少的,关于binder的文章也有很多,但是每次看总感觉看的不是很懂,到底什么才是binder机制?为什么要使用binder机制binder机制又是怎样运行的呢?这些...
  • 一、Binder机制概述 在Android开发中,很多时候我们需要用到进程间通信,所谓进程间通信,实现进程间通信的机制有很多种,比如说socket、pipe等,Android中进程间通信的方式主要有三种: 1.标准Linux Kernel IPC ...
  • 而且关于整个Binder机制的复杂程度不是三言两语能描叙清楚的,也害怕自己的理解有些偏差,误导一些朋友(ps:反正也没人看....扎心)所以也参考了很多资料,给大家脑子形成一个完整的概念,比如AIDL的实现原理,Binder...
  • Binder机制

    千次阅读 2022-01-29 20:56:09
    Binder机制 Binder是什么 binder是什么?我们都在Activity通过getSystemService()方法获取系统的Service(例如:ActivityManagerService,WindowManagerService等),这些Activity一般都是客户端编写的,而系统的...

    Binder机制

    Binder是什么

    binder是什么?我们都在Activity通过getSystemService()方法获取系统的Service(例如:ActivityManagerService,WindowManagerService等),这些Activity一般都是客户端编写的,而系统的这些Service是属于服务端的。显然它们不会在同一进程(一般来说,一个APP单独在一个进程)。两个进程之间怎么通信?Binder就是两个进程通信的中间媒介。

    知识储备

    在讲解Binder前,我们先了解一些Linux的基础知识

    进程空间划分

    • 一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内核 隔离开来
    • 进程间,内核空间的数据可共享,所以内核空间 = 可共享空间

    所有进程共用1个内核空间

    • 进程内 用户空间 & 内核空间 进行交互 需通过 系统调用,主要通过函数:
    1. copy_from_user():将用户空间的数据拷贝到内核空间
    2. copy_to_user():将内核空间的数据拷贝到用户空间

    进程隔离 & 跨进程通信( IPC )

    • 进程隔离
      为了保证 安全性 & 独立性,一个进程 不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的
    • 跨进程通信( IPC
      即进程间需进行数据交互、通信
    • 跨进程通信的基本原理

    a. 而Binder的作用则是:连接 两个进程,实现了mmap()系统调用,主要负责 创建数据接收的缓存空间 & 管理数据接收缓存Binder 跨进程通信机制 模型 基于 Client - Server 模式
    b. 注:传统的跨进程通信需拷贝数据2次,但Binder机制只需1次,主要是使用到了内存映射,具体下面会详细说明

    Binder 跨进程通信机制 模型

    模型原理图

    Binder 跨进程通信机制 模型 基于 Client - Server 模式

    模型组成角色说明

    跨进程通信的核心原理

    模型原理步骤说明

    项目实战

    如何利用Binder实现进程间通信

    我们先看下Binder调用大致原理,这是Binder调用的标准调用过程,我们下面的代码将逐渐从不标准过程转成标准过程:

    首先,我们 android studio 新建两个工程(两个 moudle 也可以,这里目的为创建两个运行在不同进程的app),一个Server,一个Client,而后,在Server中,我们新建一个java类Stub(类名无所谓),继承android.os.Binder,之后重写onTransact 方法,此处注意,onTransact方法的四个参数:

    • code:方法标识符,因为Client端对Server端的所有调用都会走到Server端的这个方法,所以理所应当Client端应该传递一个参数过来用以表示要调用哪个方法,注意这个int类型的标识必须介于 FIRST_CALL_TRANSACTIONLAST_CALL_TRANSACTION之间,所以我们给方法分配code的时候最好使用FIRST_CALL_TRANSACTION+n 这种方式
    • data :Client传递过来的序列化数据包,Parcel类型
    • reply: 如果Client端调用时需要返回值,Server通过这个对象将返回值传递回去,同样Parcel类型
    • flag 用来区分这个调用是普通调用还是单边调用,普通调用时,Client端线程会阻塞,直到从Server端接收到返回值(所以如果Client端是主线程调用,其调用的Server端不宜做耗时操作,这会让造成Client的ANR),若flag==IBinder.FLAG_ONEWAY,则这次调用是单边调用,Client在传出数据后会立即执行下一段代码,此时两端异步执行,单边调用时函数返回值必须为void (也就是异步调用必须舍弃返回值,要返回值就必须阻塞等待)

    有以上,Server端的功能就已经可以实现,但在两端通信时,为了两端Binder匹配,我们还需要在Server端做一次验证,用到data.enforceInterface(DESCRIPTOR)这个方法,DESCRIPTOR是Binder描述符,Binder Server和Client之间将通过这个描述符做验证,要想通过验证Binder通信的两端DESCRIPTOR必须相同,这也是为什么我们在使用AIDL帮助我们生成Binder代码的时候,必须把AIDL放在相同的包名下,因为SDK会根据包名为我们生成对应的DESCRIPTOR字符串,这里我们手写Binder,只需要保证两端相同就好了,包名字符串不是必须的

    那么说了一堆的原理,我们来实践一下吧

    比如我们现在有这样一个需求

    这个场景中解决的一个很常见的问题,就是我们的UI界面向service发送指令,完成界面响应对数据的控制功能

    下面为Server端完整代码

    package com.zbc.androiddevelomentartdemo.service
    
    import android.os.*
    import com.ztsc.commonutils.logcat.Logger
    import kotlin.jvm.Throws
    
    //1. 创建服务端接口
    interface IMessageServer : IInterface {
        //定义要求子类实现的接口
        @Throws(RemoteException::class)
        fun registerService()
    
        @Throws(RemoteException::class)
        fun unRegisterService()
    
        @Throws(RemoteException::class)
        fun method1(message: String?)
    
        @Throws(RemoteException::class)
        fun method2(arg1: Int, arg2: Int): Int
    
        @Throws(RemoteException::class)
        fun sendMessage(message: String?)
    
        @Throws(RemoteException::class)
        fun registerNotifyBinder(binder: IBinder)
    
        //3. 定义相关的传输协议
        companion object {
            const val DESCRIPTOR = "com.zbc.androidDevelopmentArtDemo.IMessageServer"
    
            // 数据标识最好介于FIRST_CALL_TRANSACTION - LAST_CALL_TRANSACTION 之间
            const val TRANSACTION_registerServiceNotify = Binder.FIRST_CALL_TRANSACTION + 1
            const val TRANSACTION_unregisterServiceNotify = Binder.FIRST_CALL_TRANSACTION + 2
            const val TRANSACTION_callServiceMethod = Binder.FIRST_CALL_TRANSACTION + 3
            const val TRANSACTION_method_1 = Binder.FIRST_CALL_TRANSACTION + 4
            const val TRANSACTION_method_2 = Binder.FIRST_CALL_TRANSACTION + 5
            const val TRANSACTION_registerNotifyBinder = Binder.FIRST_CALL_TRANSACTION + 6
        }
    
        //2. 服务端创建Binder对象的实现类
        abstract class Stub() : Binder(), IMessageServer {
    
            init {
                attachInterface(
                    this,
                    DESCRIPTOR
                )
            }
    
            override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
                when (code) {
                    TRANSACTION_unregisterServiceNotify -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        unRegisterService()
                        reply?.writeNoException()
                        reply?.writeString("服务端回复!\n服务已结束!")
                        //成功处理相应后返回true
                        return true
                    }
                    TRANSACTION_callServiceMethod -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        val data = data.readString()
                        Logger.e("---", "服务端接受到数据了! =  $data")
                        reply?.writeNoException()
                        reply?.writeString("服务端回复!\n老子接收到数据了! 收到的数据为 = $data")
                        //成功处理相应后返回true
                        return true
                    }
                    TRANSACTION_method_1 -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        val data = data.readString()
                        method1(data)
                        reply?.writeNoException()
                        reply?.writeString("我是服务端,老子接收到了  数据为 = $data")
                        //成功处理相应后返回true
                        return true
                    }
                    TRANSACTION_method_2 -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        //按写入的顺序读取数据
                        val result = method2(data.readInt(), data.readInt())
                        reply?.writeNoException()
                        reply?.writeString("我是服务端老子接收到了,计算了这两个目标数的乘积是 $result")
                        reply?.writeInt(result)
                        //成功处理相应后返回true
                        return true
                    }
                    TRANSACTION_registerNotifyBinder -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        //按写入的顺序读取数据
                        registerNotifyBinder(data.readStrongBinder())
                        reply?.writeNoException()
                        reply?.writeString("notifyBinder注册成功!")
                        //成功处理相应后返回true
                        return true
                    }
                }
                return super.onTransact(code, data, reply, flags)
            }
    
            override fun isBinderAlive(): Boolean {
                return super.isBinderAlive()
            }
    
            override fun asBinder(): IBinder {
                return this
            }
        }
    }
    

    应用间要实现Binder通信必须要用Service来完成,想象客户端要怎样才能知道服务端的Binder地址并向其写入数据,一种是客户端通过一个Binder地址总管查询,通过键名查找到对应的Binder服务,这种方式就是有名Binder,这个总管类就是ServiceManager,应用进程获取系统服务就是通过查询这个Binder总管实现的,比如应用进程启动进入java层后就会去查找AMS的客户端,就是通过ServiceManager来查找的,但作为应用进程,是不能向ServiceManager注册有名Binder的,所以我们的客户端也没法通过ServiceManager查询到对应的Binder服务端,但应用进程间依然是可以获取到对方的Binder服务端的,Binder并不一定要注册到ServiceManager才能被获取到,这种Binder的获取方式就是通过已经获取到的Binder传递Binder,也就是说如果有某个有名Binder服务它提供了传递Binder的方法,那么我们就可以通过这个Binder服务来传递我们的匿名Binder,正好,AMS作为一个有名Binder提供了这个功能,其对Binder传递被封装到了Service组件当中,我们可以通过Service.onBind 来返回我们要传递的Binder客户端,而在Activity.bindService中获取到这个Binder:

    class MessageServerService : Service() {
        private var mClientBinder: IBinder? = null
        private var mNotifyBinderProxy: NotifyBinderProxy? = null
    
        // 4. 创建对应的Binder实例,Server端真正实现业务的方法
        private val mBinder = object : IMessageServer.Stub() {
    
            override fun registerService() {
    
            }
    
            override fun unRegisterService() {
                this@MessageServerService.stopSelf()
            }
    
            override fun method1(message: String?) {
    
            }
    
            override fun method2(arg1: Int, arg2: Int): Int {
                return arg1 * arg2
            }
    
            override fun sendMessage(message: String?) {
                Logger.e("---", message ?: "")
            }
    
            override fun registerNotifyBinder(binder: IBinder) {
                mClientBinder = binder
                mNotifyBinderProxy = NotifyBinderProxy(mClientBinder)
            }
        }
    
        override fun onDestroy() {
            mClientBinder = null
            super.onDestroy()
        }
    
        override fun onBind(intent: Intent): IBinder? {
           
            // 4. 返回对象给客户端
            return mBinder.asBinder()
        }
    }
    

    AndroidManifest.xml中的代码

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.zbc.androiddevelomentartdemo">
     
        <application
            android:name=".application.MApplication"
            android:allowBackup="true"
            android:icon="@mipmap/list_ic_message_huodong"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">
            <service           android:name=".service.MessageServerService"
                android:enabled="true"
                android:exported="true"
                android:process=":remote" />
        </application>
    
    </manifest>
    

    那么客户端是怎么回去这个Binder服务并传递数据的呢?

    客户端实现

    想要获取服务端的binder,我们可以利用四大组件中service的bind() 函数完成和service的绑定,绑定完成后通过ServiceConnection回传对应的binder对象,之后我们可以通过这个binder对象完成向binder Server发送数据。

    /**
     * Activity与指定的Service互传数据
     */
    class PassingDataServiceActivity : AppCompatActivity() {
        private var mIBinder: IBinder? = null
    
        private var mIsConnectionBind = false
        private var mBinderProxy: BinderProxy? = null
         
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_passing_data_service)
            connectService()
            btn_act_send_data.setOnClickListener {
                connectService()
                mBinderProxy?.sendMessage("通过binder发送数据到另一个进程")
            }
            btn_act_call_method1.setOnClickListener {
                connectService()
                mBinderProxy?.method1("通过binder发送数据到另一个进程,调用约定好的method1")
            }
            btn_act_call_method2.setOnClickListener {
                connectService()
                mBinderProxy?.method2(10, 20)
            }
        }
    
        private fun connectService() {
            if (!mIsConnectionBind || mIBinder == null || !mIBinder!!.isBinderAlive) {
                bindService()
            }
        }
    
        /**
         * 绑定数据服务
         */
        private fun bindService() {
            val intent = Intent(
                this, MessageServerService::class.java
            )
             
            mIsConnectionBind = bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
            service_message.text = "服务绑定成功!等待传输数据~"
        }
    
    
        private val mConnection = object : ServiceConnection {
    
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                Logger.e("the ComponentName is :" + name?.className)
                mIBinder = service
                mBinderProxy = BinderProxy(mIBinder)
                mBinderProxy?.registerNotifyBinder(mNotifyBinder)
                liveData()
            }
    
            override fun onServiceDisconnected(name: ComponentName?) {
                mIBinder = null
                mIsConnectionBind = false
            }
        }
    
        fun liveData() {
            // Create the observer which updates the UI.
            val nameObserver = Observer<String> { newName ->
                // Update the UI, in this case, a TextView.
                service_message.text = newName
            }
    
            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            mBinderProxy?.mCurrentName?.observe(this, nameObserver)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            if (mIsConnectionBind) {
                mBinderProxy?.unRegisterService()
                mBinderProxy?.release()
                mBinderProxy == null
                unbindService(mConnection)
            }
        }
    }
    

    这里有一个小的技巧,我们不是通过IBinder 对象直接发送数据的,因为如果我们直接将代码放在Activity这个UI中就会出现通讯细节逻辑和UI界面出现耦合关系,这个在解耦思想中是不被允许的,那么怎么做呢?

    这个行业中有特定的做法,是通过将binder的通讯的内部实现封装到一个BinderProxy的代理类中,完成代理的,这样你在回过头来,你现在再去理解上面我们的代码,是不是看这很简洁,和舒服呢!

    BinderProxy类的内部实现

    package com.zbc.androiddevelomentartdemo.helper
    
    import android.os.IBinder
    import android.os.Parcel
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.ViewModel
    import com.zbc.androiddevelomentartdemo.service.IMessageServer
    import com.ztsc.commonutils.logcat.Logger
    
    /**
     * 为了隐藏Binder内部的调用,让业务无感知,
     * 一般会封装一个代理层,影藏内部的实现
     * https://www.jianshu.com/p/0fff33c09f34
     */
    class BinderProxy(var mIBinder: IBinder?) : ViewModel() {
        // Create a LiveData with a String
        val mCurrentName: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }
    
        fun sendMessage(message: String?) {
            if (!checkAvailable()) {
                return
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IMessageServer.DESCRIPTOR)
                data.writeString(message)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IMessageServer.TRANSACTION_callServiceMethod, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                val result = reply.readString()
                Logger.e("---", "接收到数据  = $result")
                mCurrentName.value = "信息打印如下: \n$result"
            }
        }
    
        fun handle(block: (_data: Parcel, _reply: Parcel) -> Unit) {
            try {
                val data = Parcel.obtain()
                val reply = Parcel.obtain()
                try {
                    block(data, reply)
                } catch (e: java.lang.Exception) {
                    Logger.e("sendMessageToService error!", e)
                } finally {
                    data.recycle()
                    reply.recycle()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    
    
        fun method1(message: String?) {
            if (!checkAvailable()) {
                return
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IMessageServer.DESCRIPTOR)
                data.writeString(message)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IMessageServer.TRANSACTION_method_1, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                val result = reply.readString()
                mCurrentName.value = "信息打印如下: \n$result"
                Logger.e("---", "接收到数据  = $result")
            }
        }
    
        fun method2(arg1: Int, arg2: Int): Int {
            var result = 0
            if (!checkAvailable()) {
                return 0
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IMessageServer.DESCRIPTOR)
                data.writeInt(arg1)
                data.writeInt(arg2)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IMessageServer.TRANSACTION_method_2, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                mCurrentName.value = "信息打印如下: \n${reply.readString()}"
                result = reply.readInt()
            }
            return result
        }
    
        fun registerNotifyBinder(binder:IBinder){
            if (!checkAvailable()) {
                return
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IMessageServer.DESCRIPTOR)
                data.writeStrongBinder(binder)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IMessageServer.TRANSACTION_registerNotifyBinder, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                val result = reply.readString()
                mCurrentName.value = "信息打印如下: \n$result"
                Logger.e("---", "接收到数据  = $result")
            }
        }
    
        fun unRegisterService(){
            if (!checkAvailable()) {
                return
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IMessageServer.DESCRIPTOR)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IMessageServer.TRANSACTION_unregisterServiceNotify, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                val result = reply.readString()
                mCurrentName.value = "信息打印如下: \n$result"
                Logger.e("---", "接收到数据  = $result")
            }
        }
    
        fun release() {
            mIBinder = null
        }
    
        private fun checkAvailable(): Boolean {
            return mIBinder?.isBinderAlive ?: false
        }
    
    }
    

    这个类的核心原理就是封装了binder通信调用的细节,而对调用方暴露的接口就类似于调用本地函数。下一步我们可以将Client端的接口和Server端的接口提取出一个公用的接口,让BinderProxy和Stub统一实现,这样就可以隐藏内部的实现细节,当我们提供给调用方的时候,开发人员就是感觉自己在调用本地的函数,完全不需要care内部负责的实现逻辑和底层原理。说到这个,大家可以详细的查看一个叫Messager的类,他的封装思想就是和这个完全一样的。

    完成双向通讯

    通过上面的栗子,我们已经成功的完成,client向Server端发送信息了,那么我们怎么来完成Server主动的推送消息给Client端呢?实现类似于服务器向客户端完成消息推送的功能。

    其实解决这个问题核心就是通过Client端已将获取的Server端的binder对象,将自己的binder对象传递给Server,Server端只要能够拿到这个Client端的Binder对象,不就可以完成消息的推送了么。

    那么怎么实现binder的反向传递呢!有一个函数是关键

    val data = Parcel.obtain()
    data.writeStrongBinder(binder)
    

    完整的代码:

    Client端传递自己的Binder给Server端

    BinderProxy.kt

    fun registerNotifyBinder(binder:IBinder){
        if (!checkAvailable()) {
            return
        }
        handle { data, reply ->
            //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
            data.writeInterfaceToken(IMessageServer.DESCRIPTOR)
            data.writeStrongBinder(binder)
            // https://www.jianshu.com/p/0fff33c09f34
            mIBinder?.transact(IMessageServer.TRANSACTION_registerNotifyBinder, data, reply, 0)
            //读取服务端的处理异常
            reply.readException()
            val result = reply.readString()
            mCurrentName.value = "信息打印如下: \n$result"
            Logger.e("---", "接收到数据  = $result")
        }
    }
    

    Client 端的服务server实现

    package com.zbc.androiddevelomentartdemo.service
    
    import android.os.*
    import kotlin.jvm.Throws
    
    
    //1. 创建服务端接口
    interface IServiceNotify : IInterface {
        //定义要求子类实现的接口
        @Throws(RemoteException::class)
        fun registerService()
    
        @Throws(RemoteException::class)
        fun unRegisterService()
    
        @Throws(RemoteException::class)
        fun callServiceMethod()
    
        @Throws(RemoteException::class)
        fun sendMessage(message: String?)
    
        //3. 定义相关的传输协议
        companion object {
            const val DESCRIPTOR = "com.zbc.androidDevelopmentArtDemo.IServiceNotify"
    
            const val TRANSACTION_registerService = Binder.FIRST_CALL_TRANSACTION + 1
            const val TRANSACTION_unRegisterService = Binder.FIRST_CALL_TRANSACTION + 2
            const val TRANSACTION_callServiceMethod = Binder.FIRST_CALL_TRANSACTION + 3
            const val TRANSACTION_sendMessage = Binder.FIRST_CALL_TRANSACTION + 4
        }
    
        //2. 服务端创建Binder对象的实现类
        abstract class Proxy() : Binder(), IServiceNotify {
    
            init {
                attachInterface(
                    this,
                    DESCRIPTOR
                )
            }
    
            override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
                when (code-) {
                    TRANSACTION_callServiceMethod -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        callServiceMethod()
                        reply?.writeNoException()
                        reply?.writeString("客户端回复!\ncallServiceMethod success!")
                        //成功处理相应后返回true
                        return true
                    }
                    TRANSACTION_sendMessage -> {
                        /**
                         * 读取由writeInterfaceToken编写的头,并验证它与相关的接口名称是否匹配。
                         * 如果出现错误的接口类型,则抛出SecurityException。
                         * 在绑定器上使用时,此异常应传播到调用者。
                         */
                        data.enforceInterface(DESCRIPTOR)
                        sendMessage(data.readString())
                        reply?.writeNoException()
                        reply?.writeString("客户端回复!\n接收到服务端发送的数据!")
                        //成功处理相应后返回true
                        return true
                    }
                }
                return super.onTransact(code, data, reply, flags)
            }
    
            override fun isBinderAlive(): Boolean {
                return super.isBinderAlive()
            }
    
            override fun asBinder(): IBinder {
                return this
            }
        }
    
    
    }
    

    PassingDataServiceActivity.kt

    package com.zbc.androiddevelomentartdemo.activity
     
    /**
     * Activity与指定的Service互传数据
     */
    class PassingDataServiceActivity : AppCompatActivity() {
        private var mIBinder: IBinder? = null
    
        private var mIsConnectionBind = false
        private var mBinderProxy: BinderProxy? = null
        //Client的binder服务对象
        private val mNotifyBinder: IBinder by lazy {
            object : IServiceNotify.Proxy() {
                override fun registerService() {
    
                }
    
                override fun unRegisterService() {
    
                }
    
                override fun callServiceMethod() {
    
                }
    
                override fun sendMessage(message: String?) {
                    runOnUiThread{
                        service_message.text = "客户端服务接受到发送过来的数据 $message"
                    }
                }
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_passing_data_service)
            connectService()
            btn_act_send_data.setOnClickListener {
                connectService()
                mBinderProxy?.sendMessage("通过binder发送数据到另一个进程")
            }
            btn_act_call_method1.setOnClickListener {
                connectService()
                mBinderProxy?.method1("通过binder发送数据到另一个进程,调用约定好的method1")
            }
            btn_act_call_method2.setOnClickListener {
                connectService()
                mBinderProxy?.method2(10, 20)
            }
        }
    
        private fun connectService() {
            if (!mIsConnectionBind || mIBinder == null || !mIBinder!!.isBinderAlive) {
                bindService()
            }
        }
    
        /**
         * 绑定数据服务
         */
        private fun bindService() {
            val intent = Intent(
                this, MessageServerService::class.java
            )
            intent.putExtras(Bundle()
                .apply {
                    putParcelable(
                        "systemMsgBean",
                        SystemMsgBean(
                            "a00001",
                            "Hello ! 我是通过bundle传递数据",
                            System.currentTimeMillis(),
                            0
                        )
                    )
                })
            mIsConnectionBind = bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
            service_message.text = "服务绑定成功!等待传输数据~"
        }
    
    
        private val mConnection = object : ServiceConnection {
    
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                Logger.e("the ComponentName is :" + name?.className)
                mIBinder = service
                mBinderProxy = BinderProxy(mIBinder)
                mBinderProxy?.registerNotifyBinder(mNotifyBinder)
                liveData()
            }
    
            override fun onServiceDisconnected(name: ComponentName?) {
                mIBinder = null
                mIsConnectionBind = false
            }
        }
    
        fun liveData() {
            // Create the observer which updates the UI.
            val nameObserver = Observer<String> { newName ->
                // Update the UI, in this case, a TextView.
                service_message.text = newName
            }
    
            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            mBinderProxy?.mCurrentName?.observe(this, nameObserver)
        }
    
        override fun onDestroy() {
            super.onDestroy()
            if (mIsConnectionBind) {
                mBinderProxy?.unRegisterService()
                mBinderProxy?.release()
                mBinderProxy == null
                unbindService(mConnection)
            }
        }
    }
    

    上面的代码是实例化这个客户端的Binder Server,并传递给了服务端。

    那么服务端的真正实现是什么样的呢?

    Server端注册自己的binder包装类

    NotifyBinderProxy.kt是包装类,放在服务端使用

    package com.zbc.androiddevelomentartdemo.helper
    
    import android.os.IBinder
    import android.os.Parcel
    import com.zbc.androiddevelomentartdemo.service.IServiceNotify
    import com.ztsc.commonutils.logcat.Logger
    
    /**
     * 客户端服务的包装类
     */
    class NotifyBinderProxy(var mIBinder: IBinder?) : IServiceNotify {
        override fun registerService() {
    
        }
    
        override fun unRegisterService() {
    
        }
    
        override fun callServiceMethod() {
    
        }
    
        fun handle(block: (_data: Parcel, _reply: Parcel) -> Unit) {
            try {
                val data = Parcel.obtain()
                val reply = Parcel.obtain()
                try {
                    block(data, reply)
                } catch (e: java.lang.Exception) {
                    Logger.e("sendMessageToService error!", e)
                } finally {
                    data.recycle()
                    reply.recycle()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    
        override fun sendMessage(message: String?) {
            if (!checkAvailable()) {
                return
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IServiceNotify.DESCRIPTOR)
                data.writeString(message)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IServiceNotify.TRANSACTION_sendMessage, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                val result = reply.readString()
                Logger.e("---", "接收到数据  = $result")
            }
        }
    
        override fun asBinder(): IBinder {
            return mIBinder!!
        }
    
        private fun checkAvailable(): Boolean {
            return mIBinder?.isBinderAlive ?: false
        }
    }
    

    服务端发送数据

    /**
     * Created by benchengzhou on 2022/1/13  14:12 .
     * 作者邮箱: mappstore@163.com
     * 功能描述: 服务service
     * 类    名: ServerService
     * 备    注:
     */
    class MessageServerService : Service() {
        private var mClientBinder: IBinder? = null
        private var mNotifyBinderProxy: NotifyBinderProxy? = null
      
        ......
    
        override fun onBind(intent: Intent): IBinder? {
            val bundle: Bundle = intent.extras as Bundle
            val systemMsgBean: SystemMsgBean? = bundle.getParcelable("systemMsgBean") as SystemMsgBean?
            Logger.e("onBind 接收到的消息为————————\n" + systemMsgBean?.toString() ?: "")
    
            GlobalScope.launch(Dispatchers.IO) {
                do {
                    //模拟服务端推送消息给客户端
                    delay(3000)
                    mNotifyBinderProxy?.sendMessage("来自服务的的数据推送")
                    Logger.e("send message!" )
                } while (mClientBinder != null)
            }
            // 4. 返回对象给客户端
            return mBinder.asBinder()
        }
    
    
    }
    

    到这里,一个完整的Service跨进程,双向通讯的实现就完成了。怎么样很简单吧,反正你记住这个模型到时候自己套用就可以了。

    如何通过上面的模型传递大文件

    那当然是通过内存共享机制了,不过中间需要借助与binder传递对应的共享文件操作符信息

    核心类MemoryFileHelper.kt

    package com.zbc.androiddevelomentartdemo.helper
    
    import android.os.*
    import android.util.Log
    import com.zbc.androiddevelomentartdemo.content.ContentValue
    import java.io.FileDescriptor
    import java.io.FileInputStream
    import java.io.IOException
    
    /**
     * 共享内存写入和读取实现
     */
    class MemoryFileHelper {
    
        companion object {
            private const val TAG = "MemoryFileHelper"
    
            /**
             * 写入匿名共享内存
             * @param data 目标数据
             * @param length  数据长度
             * @return 匿名共享内存对应的文件描述符
             */
            fun writeToMemory(data: ByteArray?, length: Int): ParcelFileDescriptor? {
                var parcelFileDescriptor: ParcelFileDescriptor? = null
                var memoryFile: MemoryFile? = null
                try {
                    memoryFile = MemoryFile("", length)
                    memoryFile.writeBytes(data, 0, 0, length)
                    val method = MemoryFile::class.java.getDeclaredMethod("getFileDescriptor")
                    method.isAccessible = true
                    val fileDescriptor = method.invoke(memoryFile) as FileDescriptor
                    parcelFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor)
                } catch (ex: Exception) {
                    Log.w(TAG, "catch write to memory error", ex)
                } finally {
                    memoryFile?.close()
                }
                return parcelFileDescriptor
            }
    
    
            /**
             * 把ParcelFileDescriptor发送到Server端
             * @param iFloatingService
             * @param data
             * @param length
             */
            fun sendToFdServer(
                binder: IBinder?,
                KEY_FD: String,
                data: ByteArray?,
                length: Int,
                width: Int,
                height: Int, block: (binder: IBinder?, bundle: Bundle) -> Unit
            ) {
                val parcelFileDescriptor: ParcelFileDescriptor =
                    writeToMemory(data, length) ?: throw Exception("writeToMemory error!")
    
                val params = java.util.HashMap<String, ParcelFileDescriptor>(1)
                params[KEY_FD] = parcelFileDescriptor
                val bundle = Bundle()
                bundle.putSerializable(
                    KEY_FD,
                    params
                )
                bundle.putInt(ContentValue.KEY_LENGTH, length)
                bundle.putInt(ContentValue.KEY_WIDTH, width)
                bundle.putInt(ContentValue.KEY_HEIGHT, height)
                block(binder, bundle)
    
            }
    
            /**
             * 读取匿名共享内存
             * @param params
             * @param length 数据结果的长度
             * @return 数据结果,null表示读取失败
             */
            fun readFromMemoryAsByteArray(
                KEY_FD: String,
                params: HashMap<String?, ParcelFileDescriptor?>,
                length: Int
            ): ByteArray? {
                val fileDescriptor = params[KEY_FD]
                fileDescriptor?.apply {
                    val data = ByteArray(length)
                    var fileInputStream: FileInputStream? = null
                    try {
                        fileInputStream = FileInputStream(fileDescriptor.fileDescriptor)
                        fileInputStream.read(data)
                        return data
                    } catch (e: java.lang.Exception) {
                        Log.w(TAG, "catch read from memory error", e)
                    } finally {
                        fileInputStream?.apply {
                            try {
                                fileDescriptor.close()
                            } catch (e: IOException) {
                                Log.w(TAG, "catch close fd error", e)
                            }
                        }
                    }
                }
                return null
            }
        }
    }
    

    binder 传递这个操作符的关键代码如下

    /**
     * 客户端服务的包装类
     */
    class NotifyBinderProxy(var mIBinder: IBinder?) : IServiceNotify {
    
    	...
    
        fun handle(block: (_data: Parcel, _reply: Parcel) -> Unit) {
            try {
                val data = Parcel.obtain()
                val reply = Parcel.obtain()
                try {
                    block(data, reply)
                } catch (e: java.lang.Exception) {
                    Logger.e("sendMessageToService error!", e)
                } finally {
                    data.recycle()
                    reply.recycle()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    
        override fun sendMessage(message: String?) {
            if (!checkAvailable()) {
                return
            }
            handle { data, reply ->
                //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                data.writeInterfaceToken(IServiceNotify.DESCRIPTOR)
                data.writeString(message)
                // https://www.jianshu.com/p/0fff33c09f34
                mIBinder?.transact(IServiceNotify.TRANSACTION_sendMessage, data, reply, 0)
                //读取服务端的处理异常
                reply.readException()
                val result = reply.readString()
                Logger.e("---", "接收到数据  = $result")
            }
        }
    
        override fun transmitMemoryFile(byteArray: ByteArray?) {
            if (!checkAvailable()) {
                return
            }
            MemoryFileHelper.sendToFdServer(
                mIBinder,
                ContentValue.KEY_FD,
                byteArray,
                byteArray?.size ?: 0,
                0,
                0
            ) { _, bundle ->
                handle { data, reply ->
                    //因为是通过parcelable传递的数据,所以写入数据的顺序和大区数据的顺序一定要一致,任何信息,数量不一致和类型不一致都会error
                    data.writeInterfaceToken(IServiceNotify.DESCRIPTOR)
                    data.writeBundle(bundle)
                    // https://www.jianshu.com/p/0fff33c09f34
                    mIBinder?.transact(IServiceNotify.TRANSACTION_transmitMemoryFile, data, reply, 0)
                    //读取服务端的处理异常
                    reply.readException()
                    Logger.e("信息打印如下: \n${reply.readString()}")
                }
            }
        }
    
        override fun asBinder(): IBinder {
            return mIBinder!!
        }
    
        private fun checkAvailable(): Boolean {
            return mIBinder?.isBinderAlive ?: false
        }
    }
    

    当然你会问,如果我仅仅期望可以单项的向Service发送控制命令,这个怎么实现呢!使用binder这种形式是不是太过于复杂了。

    简易方案,通过Messenger与Message实现binder通信

    如果我们自信的查看源码,你就会发现,Messenger其实就是帮助我们封装了底层的Binder服务实现,我们可以基于这个封装完成简单的IPC通讯

    Messenger.java

    public final class Messenger implements Parcelable {
        private final IMessenger mTarget;
    }
    
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
    

    其中的IMessager是一个AIDL

    看完Message的源码,你或许就会恍然大悟了,这不就是完全复制的我们的BInder的那套东西么。

    代码实战

    依旧是这个小栗子

    废话不多说直接上代码。

    UI界面端

    package com.zbc.androiddevelomentartdemo.activity
    
    import android.content.ComponentName
    import android.content.Context
    import android.content.Intent
    import android.content.ServiceConnection
    import android.os.*
    import androidx.appcompat.app.AppCompatActivity
    import com.zbc.androiddevelomentartdemo.R
    import com.zbc.androiddevelomentartdemo.entity.SystemMsgBean
    import com.zbc.androiddevelomentartdemo.entity.UserBean
    import com.zbc.androiddevelomentartdemo.service.RemoteServerService
    import com.ztsc.commonutils.logcat.Logger
    import kotlinx.android.synthetic.main.activity_transform_data_to_service.*
    
    /**
     * bidner  简单完成传递数据给Service
     */
    class TransformDataServiceActivity : AppCompatActivity() {
        companion object {
            private const val CODE_BASE = 0
            const val CODE_REPLAY = CODE_BASE + 1
        }
    
        private var isBindServiceSuccess = false
        private var mServiceMessenger: Messenger? = null
        private val mHandler =
            object : Handler(Looper.getMainLooper()) {
                override fun handleMessage(msg: Message) {
                    when (msg.what) {
                        CODE_REPLAY -> {
                            service_message.text ="${msg.data?.getString("message")}"
                        }
                    }
                }
            }
        private val mMessager = Messenger(mHandler)
    
    
        private val mServiceConnection by lazy {
            object : ServiceConnection {
                override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                    mServiceMessenger = Messenger(service)
                    service_message.text = "服务绑定成功!"
                }
    
                override fun onServiceDisconnected(name: ComponentName?) {
    
                }
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_transform_data_to_service)
            bindService(
                Intent().apply {
                    setAction("service.RemoteServerService")
                    setPackage(this@TransformDataServiceActivity.packageName)
                },
                mServiceConnection,
                Context.BIND_AUTO_CREATE
            )
            btn_act_send_data.setOnClickListener {
                val mess = Message.obtain()
                mServiceMessenger?.send(mess.apply {
                    what = RemoteServerService.CODE_BINDER_REGISTER
                    data = Bundle().apply {
                        putString("data","注册binder")
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                            putBinder("binder", mMessager.binder)
                        }
                    }
    
                    replyTo = mMessager
                })
                mess.recycle()
            }
        }
    
        override fun onDestroy() {
            super.onDestroy()
            if (isBindServiceSuccess) {
                mServiceMessenger = null
            }
        }
    
    }
    

    remote Server端

    package com.zbc.androiddevelomentartdemo.service
    
    import android.app.Service
    import android.content.Intent
    import android.os.*
    import com.zbc.androiddevelomentartdemo.activity.TransformDataServiceActivity
    import com.ztsc.commonutils.logcat.Logger
    
    /**
     * 模拟远程的service组件
     */
    class RemoteServerService : Service() {
        companion object {
            private const val CODE_BASE = 0
            const val CODE_BINDER_REGISTER = CODE_BASE + 1
            const val CODE_CALL_METHOD = CODE_BASE + 2
        }
    
        private var mNotifyMessenger: Messenger? = null
    
        private val mHandler by lazy {
            object : Handler(Looper.getMainLooper()) {
                override fun handleMessage(msg: Message) {
                    when (msg.what) {
                        CODE_BINDER_REGISTER -> {
                            val reply = Message.obtain()
                            msg.data?.apply {
                                //如果本次通讯是跨进程的数据传递,一定要设置classLoader,否则会因找不到可用的classLoader而报错 Parcel: Class not found when unmarshalling: com.zbc.androiddevelomentartdemo.entity.SystemMsgBean
                                classLoader = javaClass.classLoader
    
                                get("binder")?.apply {
                                    mNotifyMessenger = Messenger(this as IBinder)
                                }
                                Logger.e("Service接收到信息!${getString("data")}")
                            }
    
                            msg.replyTo.send(reply.apply {
                                what = TransformDataServiceActivity.CODE_REPLAY
                                data = Bundle().apply { putString("message", "binder服务双向注册成功!") }
                                //请不要向obj中存放数据,或crash 无法序列化
                                //obj ="xxxx"
                            })
                            reply.recycle()
                        }
                    }
                }
            }
        }
        val mMessager = Messenger(mHandler)
    
    
        override fun onBind(intent: Intent?): IBinder? {
            return mMessager.binder
        }
    
        override fun onDestroy() {
            super.onDestroy()
            mHandler.removeCallbacksAndMessages(null)
        }
    }
    

    注意:

    Messenger底层也是通过AIDL完成的,只不过代理成了Messager最终对用户的数据暴露为我们Handler、和Message的形式。

    Message中请不要将数据存放在obj中,会因为底层无法控制序列化顺序导致报错

    通过Message传递Bundle对象的时候,如果是IPC通讯,需要指定Bundle的ClassLoader

    //如果本次通讯是跨进程的数据传递,一定要设置classLoader,否则会因找不到可用的classLoader而报错 Parcel: Class not found when unmarshalling: com.zbc.androiddevelomentartdemo.entity.SystemMsgBean
    classLoader = javaClass.classLoader
    

    完整的demo演示下载链接:

    https://github.com/benchegnzhou/AndroidDevelomentArtDemo

    文档参考:

    https://www.jianshu.com/p/0fff33c09f34

    https://www.jianshu.com/p/f9a8f1747358

    展开全文
  • Android Binder机制

    2018-02-26 15:55:18
    Android Binder机制 总共11篇博客。详细介绍BINDER的内部机制
  • Android——Binder机制

    千次阅读 多人点赞 2021-06-04 16:47:21
    此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信,Interprocess Communication)方案。 也存在部分其他的IPC方式,如管道、SystemV、Socket等 为什么使用Binder: 性能方面: Binder相对于...

    1.简介

    Binder是什么?

    • 机制:Binder是一种进程间通信的机制

    • 驱动:Binder是一个虚拟物理设备驱动

    • 应用层:Binder是一个能发起进程间通信的JAVA类

    • Binder就是Android中的血管,在Android中我们使用Activity,Service等组件都需要和AMS(system_server)进行通信,这种跨进程的通信都是通过Binder完成。

    • Activity,Service等组件和AMS不是同一个进程,其实也是多进程通信。

    为什么要用多进程?

    • 虚拟机给每一个进程分配的内存是有限制的,LMK会优先回收对系统资源占用多的进程
    • 为了突破内存限制,防止占用内存过多被杀
    • 功能稳定性,一个进程崩溃对另外进程不造成影响:将不稳定功能放入独立进程
    • 规避内存泄漏,独立的WebView进程阻隔内存泄漏导致问题

    Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制。

    • 例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC。不仅于
      此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信,Interprocess Communication)方案。
    • 也存在部分其他的IPC方式,如管道、SystemV、Socket等

    进程隔离:

    • 操作系统中,进程与进程间内存是不共享的。两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离。
    • A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。

    为什么使用Binder:

    性能方面:

    • Binder相对于传统的Socket方式,更加高效
    • Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
      在这里插入图片描述

    安全方面:

    • 传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信的IP地址是客户端手动填入,很容易进行伪造
    • Binder机制从协议本身就支持对通信双方做身份校检,从而大大提升了安全性。

    2. Binder原理

    2.1 Binder原理概述

    2.1.1 IPC原理:

    从进程角度来看IPC(Interprocess Communication)机制
    在这里插入图片描述
    进程空间划分:用户空间(User Space) ——内核空间(Kernel Space)

    • 每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。例如,对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,内核空间的大小是可以通过参数配置调整的
    • 对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的
    • Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的
    • Client端与Server端进程往往采用ioctl等方法与内核空间的驱动进行交互。
      (ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。)

    只有系统调用才能操作内核空间:

    系统调用:用户态与内核态

    • 虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。
    • 为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。
    • Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用。
    copy_from_user() //将数据从用户空间拷贝到内核空间
    copy_to_user() //将数据从内核空间拷贝到用户空间
    

    2.1.2 Binder IPC 实现原理:

    动态内核可加载模块&&内存映射:
    如前面所说,跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分

    Binder 并不是 Linux 系统内核的一部分,那怎么办呢?

    于是有了Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制:

    • 模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行
    • Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动

    Android 系统中用户进程之间是如何通过这个内核模块(Binder 驱动)来实现通信的呢?当然不是之前的:将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程

    而是通过内存映射:

    • 内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间
    • 反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动

    Binder IPC 实现原理:

    • Binder IPC 正是基于内存映射(mmap)来实现的,但是 mmap() 通常是用在有物理介质的文件系统上,进程中的用户区域是不能直接和物理设备打交道的.
    • 如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘–>内核空间–>用户空间),通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率

    Binder一次拷贝原生实现:

    img


    副本直接copy进内核态映射给B用,那么什么是内存映射

    内存映射:

    • 底层原理其实就是虚拟内存

    • 简单来说:不同的虚拟内存指向相同的物理内存,从而实现共享内存和共享文件

    • 涉及外存和页交换,起因就是每个进程的地址空间都是0-L4的max,但是很多进程共用L4,这时候就需要拿L5来暂存L4暂时不用的数据和代码段,L4不用的换到L5,L5要用的换进L4就叫页交换

      • 现代多核CPU:

        • L0寄存器

        • L1是指令cache和数据cache

        • L2是单核总cache

        • L3是所有核的总cache

        • L4是内存

        • L5是硬盘,磁盘等

        • L6是通过网络挂载的远程文件系统

    • 操作系统默认是指向不同的地址,L4不够了就给你指到L5去,L5就是外存,所以有C盘空间不够了电脑卡这个说法(变量malloc不出来,阻塞了)

    • 很多进程一起跑慢是因为频繁页交换,那么一起跑为啥会导致频繁的页交换呢?

      • 页交换:内存不足时,把进程传输到磁盘上,增加了磁盘I/O的负载。页交换就是把内存页往外换,把要运行的内存换进来
      • 假设你4G的电脑,8个运行时稳定占用2G的进程在跑,假设之前在跑的进程1和2,这样这两哥把4G用完了
      • 接下来跑进程3,就需要页交换把进程3稳定运行时需要的2G换进来,1和2换出去2G
      • 接下来跑进程4,4进来2G,1,2,3出去2G,内存和硬盘频繁IO,那必须慢啊
      • 严重不足的时候,需要把赖着不走不运行的全赶走才有内存运行需要的代码啊,比如我内存就剩500KB,接下来要跑英雄联盟,那可不得把其他人都换出去(IO操作是很耗时的)

    在这里插入图片描述

    一次完整的Binder IPC 通信过程通常是这样:

    • Binder 驱动在内核空间创建一个数据接收缓存区;
    • 然后在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
    • 发送方进程通过系统调用 copy_from_user() 将数据拷贝到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

    2.2 Linux 下的传统 IPC 通信原理

    在这里插入图片描述
    传统的 IPC 方式中,进程之间是如何实现通信的:

    • 消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态,然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中
    • 接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信

    问题:

    • 性能低下:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
    • 接收数据的缓存区由数据接收进程提供,但是不知道数据大小,只能开辟大空间或者提前调用API接受消息头获取,不是浪费空间就是浪费时间。

    2.3 Binder 通信模型

    介绍完 Binder IPC 的底层通信原理,继续看看实现层面是如何设计的。

    Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及Binder驱动,其中ServiceManager用于管理系统中的各种服务。架构图如下所示:

    ioctl(input/output control)是一个专用于设备输入输出操作的系统调用
    在这里插入图片描述

    2.3.1 Binder通信的四个角色:

    • Client进程:使用服务的进程。
    • Server进程:提供服务的进程。
    • ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
    • Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

    2.3.2 Binder运行机制

    图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

    • 注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。
    • 获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
    • 使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:Client是客户端,Server是服务端。

    图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信(Interprocess Communication)方式。

    其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。

    Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现Client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。
    在这里插入图片描述
    Client、Server、ServiceManager、Binder 驱动 就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder 驱动)之前的关系。

    访问一个网页的步骤:在浏览器输入一个地址如 “http://www.google.com” 然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问 DNS 域名服务器,域名服务器中保存了 “http://www.google.com” 对应的 IP地址 “192.168.245.123”,然后通过这个 IP地址才能放到到 “http://www.google.com” 对应的服务器。

    2.3.3 Binder驱动

    • Binder 驱动就如同路由器一样,是整个通信的核心;
    • 驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

    2.3.4 ServiceManager 与实名 Binder

    • ServiceManager :ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用
    • 实名 Binder:注册了名字的 Binder 叫实名 Binder,就像网站一样除了除了有 IP 地址意外还有自己的网址
    • Server 创建了 Binder,并为它起一个字符形式将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder
    • 驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。

    注意:
    在这里插入图片描述

    • 有个很有意思的点,你可能会发现,ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。
    • 当前实现进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋
    • Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋。ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 实体,其他进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。
    • ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册,当一个进程使用 BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)。
    • 其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。类比互联网,0 号引用就好比是域名服务器的地址,你必须预先动态或者手工配置好。要注意的是,这里说的 Client 是相对于 ServiceManager 而言的,一个进程或者应用程序可能是提供服务的 Server,但对于 ServiceManager 来说它仍然是个 Client。

    2.3.5 Client 获得实名 Binder 的引用:

    • Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用了,Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder: 我申请访问名字叫张三的 Binder 引用, ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为回复发送给发起请求的 Client
    • 从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起请求的 Client 中如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。

    2.3.6 Binder 通信过程

    在这里插入图片描述

    • 1、首先,一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;

    • 2、Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。

    • 3、Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

    3. 实例

    程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:

    	//获取WindowManager服务引用
    	WindowManager wm = (WindowManager) getSystemService(getApplication().WINDOW_SERVICE);
    	//布局参数layoutParams相关设置略...
    	View view = LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
    	//添加view
    	wm.addView(view, layoutParams);
    

    注册服务(addService): 在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。这一步是系统自动完成的。

    获取服务(getService): 客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service引用的代理对象,对数据进行一些处理操作。即第2行代码中,得到的wm是WindowManager对象的引用。

    使用服务: 通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。

    在这里插入图片描述

    1. Client通过获得一个Server的代理接口,对Server进行调用。
    2. 代理接口中定义的方法与Server中定义的方法是一一对应的。
    3. Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打
      包成Parcel对象。
    4. 代理接口将Parcel发送给内核中的Binder Driver。
    5. Server会读取Binder Driver中的请求数据,如果是发送给自己的,解包Parcel
      对象,处理并将结果返回。
    6. 整个的调用过程是一个同步过程,在Server处理的时候,Client会Block住。因
      此Client调用过程不应在主线程。
    展开全文
  • AndroidBinder机制介绍

    2021-02-26 02:15:38
    做过Android开发的同学可能有些体会,入门初期,工作内容主要是实现各式各样的UI界面,以及实现应用的业务逻辑。...我们平时使用最多的startActivity、startService都是通过binder机制与AMS所在进程进行通
  • 本文是第一篇,首先会对整个Binder机制做一个架构性的讲解,然后会将大部分精力用来讲解Binder机制中最核心的部分:Binder驱动的实现。Binder源自BeInc公司开发的OpenBinder框架,后来该框架转移的PalmInc,由...
  • 一文分析Binder机制和AIDL的理解

    千次阅读 2022-03-04 17:24:33
    为什么要去理解Android的进程间通信机制 对于Android开发工程师来说,如果不去理解进程间通信机制也可以使用系统提供的API完成应用开发,但如果想要达到更高的层级,那么就不能简单只会...为什么使用Binder作为Android

    为什么要去理解Android的进程间通信机制

    对于Android开发工程师来说,如果不去理解进程间通信机制也可以使用系统提供的API完成应用开发,但如果想要达到更高的层级,那么就不能简单只会调用API。无论是工作中遇到一些疑难问题,还是想要学习源码的一些功能实现,或者是想要提升APP的性能等,这些工作都需要我们去看系统的源码,而系统的源码中进程间通信无处不在,如果不理解进程间通信机制,那么很难看懂系统源码,而且容易迷失在大量的代码中。

    Android 进程间通信机制

    为什么使用Binder作为Android进程间通信机制

    主要是为了弥补Linux中其他进程间通信方式得性能和安全性不足。当然Binder机制也并非是谷歌为了Android原创技术,Binder机制源于OpenBinder,OpenBinder要早于Android系统出现。所以如果想要立即Android得进程间通信,主要就是理解Binder机制。

    Binder进程间通信基本框架

    img

    在Android中,2个应用或者进程之间的通信都需要经过Binder代理,二者不能直接通信,同样APP在使用系统服务时也需要跨进程通信,比如我们最常用的ActivityManagerService(AMS)也是一个系统服务进程,此外APP使用WIFI 、定位、媒体服务等都是系统进程,APP想要使用这些系统服务的功能一定要通过Binder进行通信。

    Binder到底是什么

    我们一直在说利用Binder机制进行进程间通信,那么Binder具体是什么?是一个Java类,还是一个底层驱动?通常我们说Binder机制是Android系统不同层Binder相关代码组成的一套跨进程通信功能。Binder机制相关代码从最底层的驱动层到最顶层的应用层都有,所以要读懂Binder机制,就需要我们耐心的逐层进行分析。

    img

    Binder机制代码结构

    如何理解AIDL

    我们从上图没有看到任何AIDL相关的信息,也就是说Binder机制是与AIDL无关的,那么我们日常中如果要跨进程都要写一个AIDL类然后由AS生成一些Java类,我们使用这些类实现进程间通信,这么做的目的其实是由AS帮我们生成一些模板代码,减少我们的工作和出错概率,其实不用AIDL我们也可以实现Binder通信,并且可以更好的理解Binder机制。下面我写一个Demo进程,这个Demo中有AIDL文件并生成相关代码,但我们不用,只是用来作为对比,我们用最少的代码实现Binder通信,并通过对比我们写的代码和AIDL生成的代码来更好的理解AIDL生成的代码的作用。代码Github

    不使用ADIL,手动实现进程间通信

    img

    项目结构

    代码中client为客户端,server为服务端

    img

    img

    客户端进程发送一个字符串给服务端,服务端进程接收到将字符显示到界面上。项目中没有用到AIDL为我们生成Binder通信类,而是用最简单的方式实现Binder通信,因而我们可以看清Binder通信最关键的地方。首先我们要知道,实现了IBinder接口的类的对象是可以跨进程传递的。

    服务端

    1.服务端RemoteService继承Service 2.创建一个继承Binder的类ServerBinder,并覆写onTransact方法,用于处理Client的调用,Binder实现了IBinder接口 3.服务端覆写Service的onBind方法,返回一个ServerBinder对象(这个ServerBinder对象是最终传递给Client端)

    public class RemoteService extends Service {
        public static final int TRANSAVTION_showMessage = IBinder.FIRST_CALL_TRANSACTION;
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new ServerBinder();
        }
    
        static class ServerBinder extends Binder   {
            public ServerBinder() {
            }
    
            @Override
            protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
    
                switch (code) {
                    case TRANSAVTION_showMessage:
                        String message = data.readString();
                        Log.d("ServerBinder", "showMessage " + message);
                        if (ServerMainActivity.tvShowMessage != null) {//显示收到数据逻辑
                            new Handler(Looper.getMainLooper()).post(new Runnable() {
                                @Override
                                public void run() {
                                    ServerMainActivity.tvShowMessage.setText(message);
                                }
                            });
                        }
                        if (reply != null) {
                            reply.writeNoException();
                        }
                        return true;
                }
                return super.onTransact(code, data, reply, flags);
            }
    
    
        }
    }
    

    客户端

    1.客户端创建一个ServiceConnection对象,用于与服务端建立连接,并获取到服务端的IBinder对象 2.客户端通过bindService与服务端的RemoteService建立连接

    public class ClientMainActivity extends AppCompatActivity {
        private Button mSendString;
        private EditText mStingEditText;
        public static final int TRANSAVTION_showMessage = IBinder.FIRST_CALL_TRANSACTION;
        private IBinder mServer;//服务端的Binder对象
        private boolean isConnection = false;
    
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                
                isConnection = true;
                mServer = service;//建立连接成功,保存服务端进程的IBinder对象
                Log.d("Client"," onServiceConnected success");
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                isConnection = false;
            }
        };
    
        //与服务端进程中RemoteService建立连接
        private void attemptToBindService() {
            Intent intent = new Intent();
            intent.setClassName("com.binder.server", "com.binder.server.RemoteService");
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mSendString = findViewById(R.id.btn_send_string);
            mStingEditText = findViewById(R.id.et_string);
            mSendString.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (!isConnection) {
                        attemptToBindService();
                        return;
                    }
                    //发送数据给服务端进程
                    Parcel data = Parcel.obtain();
                    Parcel replay = Parcel.obtain();
                    if (mServer != null) {
                        try {
                            data.writeString(mStingEditText.getText().toString());
                            Log.d("Client"," mServer.transact call");
                          //发送数据到服务端进程
                            mServer.transact(TRANSAVTION_showMessage, data, replay, 0);
                            replay.readException();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        } finally {
                            replay.recycle();
                            data.recycle();
                        }
                    }
    
    
                }
            });
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            if (!isConnection) {
                attemptToBindService();
            }
        }
    

    从上面的代码来看Binder的跨进程通信核心就是客户端获取到服务端的IBinder对象,然后调用这个对象的transact方法发送数据,实现进程间通信。

    使用ADIL生成相关类,进行进程间通信

    img

    加入AIDL文件

    interface IShowMessageAidlInterface {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
       void showMessage(String msg);
    }
    

    修改Client端代码

    public class ClientMainActivityUseAidl extends AppCompatActivity {
        private Button mSendString;
        private EditText mStingEditText;
        private IShowMessageAidlInterface mServer;//服务端的Binder对象代理
        private boolean isConnection = false;
    
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                isConnection = true;
                //调用IShowMessageAidlInterface.Stub.asInterface静态方法,将service转换为一接口
                mServer = IShowMessageAidlInterface.Stub.asInterface(service);
                Log.d("Client"," onServiceConnected success");
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                isConnection = false;
            }
        };
        private void attemptToBindService() {
            Intent intent = new Intent();
            intent.setClassName("com.binder.server", "com.binder.server.RemoteServiceUseAidl");
            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mSendString = findViewById(R.id.btn_send_string);
            mStingEditText = findViewById(R.id.et_string);
            mSendString.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (!isConnection) {
                        attemptToBindService();
                        return;
                    }
                    try {
                      //直接调用接口的showMessage方法
                        mServer.showMessage(mStingEditText.getText().toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            if (!isConnection) {
                attemptToBindService();
            }
        }
    

    1.客户端利用 IShowMessageAidlInterface生成类中的Stub内部类将接受到的IBinder对象转换为一个接口 2.在发送数据时,直接调用IShowMessageAidlInterface接口的showMessage方法

    asInterface方法

       public static com.binder.server.IShowMessageAidlInterface asInterface(android.os.IBinder obj)
       {
         if ((obj==null)) {
           return null;
         }
       //查询obj对象是否是本地接口,也就是是不是在同一个进程
         android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
         if (((iin!=null)&&(iin instanceof com.binder.server.IShowMessageAidlInterface))) {
           如果是同一个进程直接返回
           return ((com.binder.server.IShowMessageAidlInterface)iin);
         }
     //如果是不同进程,则将IBinder对象利用Proxy封装一层
         return new com.binder.server.IShowMessageAidlInterface.Stub.Proxy(obj);
       }
    

    Proxy类

     private static class Proxy implements com.binder.server.IShowMessageAidlInterface
        {
          private android.os.IBinder mRemote;
          Proxy(android.os.IBinder remote)
          {
            mRemote = remote;
          }
          @Override public android.os.IBinder asBinder()
          {
            return mRemote;
          }
          public java.lang.String getInterfaceDescriptor()
          {
            return DESCRIPTOR;
          }
          /**
               * Demonstrates some basic types that you can use as parameters
               * and return values in AIDL.
               */
          //代理对象做的工作是把AIDL接口中定义的方法中的数据进行封装,方便进行跨进程传输
          @Override public void showMessage(java.lang.String msg) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              _data.writeString(msg);
              boolean _status = mRemote.transact(Stub.TRANSACTION_showMessage, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().showMessage(msg);
                return;
              }
              _reply.readException();
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
          public static com.binder.server.IShowMessageAidlInterface sDefaultImpl;
        }
    

    所以我们可以知道,客户端用到了AIDL文件生成Stub类中的asInterface方法,把接收到的远程IBinder转换为IShowMessageAidlInterface接口,而这个接口的具体实现其实是Proxy类,代理类对方法传入数据进行封装,然后发送给mRemote 服务端。

    修改服务端代码

    public class RemoteServiceUseAidl extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new IShowMessageAidlInterface.Stub() {
                @Override
                public void showMessage(String msg) throws RemoteException {
                    if (ServerMainActivity.tvShowMessage != null) {
                        new Handler(Looper.getMainLooper()).post(new Runnable() {
                            @Override
                            public void run() {
                                ServerMainActivity.tvShowMessage.setText(msg);
                            }
                        });
                    }
                }
            };
        }
    }
    
    
    

    服务端的 onBind方法返回AIDL生成的Stub类的对象,Stub是个抽象类,其中待实现的方法为AIDL中定义的showMessage方法。

     public static abstract class Stub extends android.os.Binder implements com.binder.server.IShowMessageAidlInterface
      {
        private static final java.lang.String DESCRIPTOR = "com.binder.server.IShowMessageAidlInterface";
        static final int TRANSACTION_showMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
          this.attachInterface(this, DESCRIPTOR);
        }
        @Override public android.os.IBinder asBinder()
        {
          return this;
        }
        @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)
          {
            case INTERFACE_TRANSACTION:
            {
              reply.writeString(descriptor);
              return true;
            }
            case TRANSACTION_showMessage:
            {
              data.enforceInterface(descriptor);
              java.lang.String _arg0;
              _arg0 = data.readString();
              this.showMessage(_arg0);
              reply.writeNoException();
              return true;
            }
            default:
            {
              return super.onTransact(code, data, reply, flags);
            }
          }
        }
       
      }
    
    
    

    可以看到Sub抽象类中继承自Binder,也就是客端最终拿到的是这个Stub IBinder对象,客户端调用tansact方法最终会调用到Stub类的onTransact进行处理,Stub的onTransact方法根据code确定客端户调用了哪个方法,然后对接收到的data数据进行读取解析,将处理好的数据交给IShowMessageAidlInterface中对应的方法。

    总结: 1.AIDL生成的类中Stub的静态方法asInterface和Proxy类是给客户端用于发送数据的 2.Stub抽象类是由服务端实现,接收处理客户端数据的

    展开全文
  • Binder机制原理

    千次阅读 2022-04-14 15:11:16
    Binder是什么? 可以理解为Android的血管。是一种进程间通信的机制。比如Activity,Service需要和AMS...Linux也有一些很优秀的进程间通信机制,例如管道,消息队列,共享内存,socket等,但是为什么Android使用Binder
  • Binder 机制 的简单理解和使用Binder 机制参考 ...1、图解Android中的Binder机制 2、Android Binder(也许是最容易理解的) 3、图文详解Binder跨进程通信原理 4、Android 接口定义语言 (AIDL) ...
  • 深入理解Binder机制

    2022-06-30 11:03:27
    1.Binder是一种进程间通信机制; 2.Binder是一个虚拟物理设备驱动; 3.Binder是一个能发起通信的Java类;在了解传输流程之前,先简单了解下Android中的内存划分以及内存映射【mmap】概念; ####内存划分 内存被操作...
  • 详见http://blog.csdn.net/huaxun66/article/details/52966087
  • 【一图流】_02_一张图看懂 Android 进程间通信(IPC)Binder机制:  此图表述了Android系统_进程间通信(IPC)机制全部体系,其中重点放在 Android系统中 重用 的 Binder机制 上,详尽细致,希望对大家有用;
  • 深刻理解Binder机制

    千次阅读 2021-12-02 21:12:52
    Binder机制其实是用于进程间通信的,可以理解为进程之间的粘合剂。 Binder到底是什么? 从机制,模型角度来说:Binder是一种Android中实现跨进程通信(IPC)的方式,用于实现Android中跨进程通信。【Binder机制】 ...
  • 不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案。 当然也存在部分其他的IPC方式,如管道、SystemV、Socket等。那么Android为什么不使用这些原有的技术,而是要使开发一种新的叫...
  • Android进程间通信,详细描述了android binder机制实现流程;
  • 这篇文章会先对比Binder机制与Linux的通信机制的差别,了解为什么Android会另起炉灶,采用Binder。 接着,会根据 Binder的机制,去理解什么是Service Manager,在C/S模型中扮演什么角色。最后,会从 一次完整的通信...
  • 前文中我们看到,Binder机制在C++层已经有了完整的实现。因此Java层完全不用重复实现,而是通过JNI衔接了C++层以复用其实现。下图描述了BinderFrameworkJava层到C++层的衔接关系。这里对图中Java层和JNI层的几个类做...
  • Android深入浅出之Binder机制 一 说明  Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的。所以搞明白Binder的话,在很大程度上就能理解程序运行的...
  • 应用里面哪些地方用到了binder机制? 应用的大致启动流程是怎样的? 一个进程是怎么启动binder机制的? 应用启动流程 什么时候支持binder机制的? boolean runOnce(){ String[] args = readArgumentList(); int ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,635
精华内容 19,054
关键字:

binder机制