Android Framework系列 Binder

一 Binder概述

简单介绍下什么是 Binder。Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现;OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手。Hackborn加入谷歌后,他在OpenBinder 的基础上开发出了 Android Binder (以下简称 Binder),用来完成 Android 的进程通信。

1 为什么要学习Binder

作为一个Android开发者,其实我们每天都会和Binder打交道,只不过有时候我们不会注意到,譬如:

  • startActivity的时候,传递的对象为什么需要序列化,是如何调用到AMS服务的startActivity方法的。
  • bindService为什么回调的是一个Ibinder对象。
  • 多进程应用,各个进程之间是如何通信的。
  • AIDL是如何使用的。
  • 应用程序是如何和系统进程进行通信的。
它们都和 Binder 有着莫切关系,当碰到上面的场景,或者一些疑难问题的时候,理解 Binder 机制是非常有必要的。我们知道 Android 应用程序是由 Activity Service Broadcast Receiver Content Provide 四大组件中的一 个或者多个组成的。有时这些组件运行在同一进程,有时运行在不同的进程。这些进程间的通信就依赖于 Binder IPC机制。不仅如此, Android 系统对应用层提供的各种服务如: ActivityManagerService PackageManagerService 等都是基于 Binder IPC 机制来实现的。 Binder 机制在 Android 中的位置非常重要,毫不夸张的说理解 Binder 是迈向 Android 高级工程的第一步。

2 为什么选择Binder

我们知道在Linux已经提供了管道、消息队列、共享内存和Socket等IPC机制。Android系统是基于Linux内核的,那么为什么Android还要提供Binder来实现IPC呢?主要是基于性能、稳定性和安全性几个方面的原因。我们先来看看其他几种机制的优缺点然后和Binder进行对比。

2.1 管道
管道分为命名管道和无名管道,它是以一种特殊的文件作为中间介质,我们称为管道文件,它具有固定的读端和写端,写进程通过写段向管道文件里写入数据,读进程通过读段从读进程中读出数据,构成一条数据传递的流水线,它的原理如下图所示:
管道一次通信需要经历 2 次数据复制(进程 A -> 管道文件,管道文件 -> 进程 B )。管道的读写分阻塞和非阻塞,管道创建会分配一个缓冲区,而这个缓冲区是有限的,如果传输的数据大小超过缓冲区上限,或者在阻塞模式下没有安排好数据的读写,会出现阻塞的情况。管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式。

2.2 消息队列
消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示。消息队列允许多个进程同时读写消息,发送方与接收方要约定好消息体的数据类型与大小。消息队列克服了信号承载信息量少、管道只能承载无格式字节流等缺点,消息队列一次通信同样需要经历2 次数据复制(进程 A -> 消息队列,消息队列 -> 进程 B ),它的原理如下图所示:
2.3 Socket
Socket 原本是为了网络设计的,但也可以通过本地回环地址 ( 127.0.0.1 ) 进行进程间通信,后来在 Socket 的框架上更是发展出一种IPC 机制,名叫 UNIX Domain Socket Socket 是一种典型的 C/S 架构, 一个 Socket 会拥有两个缓冲区,一读一写,由于发送/ 接收消息需要将一个 Socket 缓冲区中的内容拷贝至另一个 Socket 缓冲区,所以Socket一次通信也是需要经历 2 次数据复制,它的原理如下图所示:

 

2.4 内存共享 
共享内存是进程间通信中最简单的方式之一,共享内存允许两个或更多进程访问同一块内存,当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改,它的原理如下图所示:
因为共享内存是访问同一块内存,所以数据不需要进行任何复制,是 IPC 几种方式中最快,性能最好的方式。但相对应的,共享内存未提供同步机制,需要我们手动控制内存间的互斥操作,较容易发生问题。同时共享内存由于能任意的访问和修改内存中的数据,如果有恶意程序去针对某个程序设计代码,很可能导致隐私泄漏或者程序崩溃,所以安全性较差。
2.5 Binder
由上面我们知道传统的IPC机制都是Linux内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是Binder 并不是Linux系统内核的一部分,那么怎么办呢? 这就得益于 Linux
态内核可加载模块 Loadable Kernel Module LKM )的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。这个内核模块就叫Binder驱动。
Binder驱动是如何来实现心通信的呢?它其实是通过内存映射来实现的。
内存映射
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,  mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。
内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。Binder IPC 正是基于内存映射( mmap )来实现的,但是 mmap() 通常是用在有物理介质的文件系统上的。
但是进程中的用户区域是不能直接和物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘--> 内核空间 --> 用户空间);通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O 读写,提高文件读取效率。
Binder 并不存在物理介质,因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。
一次完整的 Binder IPC 通信过程通常是这样:
1. 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
2. 接着在内核空间开辟一块内核缓存区,建立 内核缓存区 内核中数据接收缓存区 之间的映射关系,以及 内核中 数据接收缓存区 接收进程用户空间地址 的映射关系;
3. 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的 内核缓存区, 由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
如下图:
总结 
性能
首先说说性能上的优势。 Socket 作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储- 转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。 Binder 只需要一次数据拷贝,性能上仅次于共享内存。
稳定性
再说说稳定性, Binder 基于 C/S 架构,客户端( Client )有什么需求就丢给服务端( Server )去完成,架构清晰、职责明确又相互独立,自然稳定性更好。共享内存虽然无需拷贝,但是控制负责,难以使用。从稳定性的角度讲,Binder 机制是优于内存共享的。
安全性
另一方面就是安全性。 Android 作为一个开放性的平台,市场上有各类海量的应用供用户选择安装,因此安全性对于Android 平台而言极其重要。作为用户当然不希望我们下载的 APP 偷偷读取我的通信录,上传我的隐私数据,后台偷跑流量、消耗手机电量。传统的 IPC 没有任何安全措施,完全依赖上层协议来确保。首先传统的 IPC 接收方无法获得对方可靠的进程用户ID/ 进程 ID UID/PID ),从而无法鉴别对方身份。 Android 为每个安装好的 APP 分配了自己的 UID ,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID ,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。同时 Binder 既支持实名 Binder ,又支持匿名 Binder ,安全性高。
对比如如下:
基于上述原因, Android 需要建立一套新的 IPC 机制来满足系统对稳定性、传输性能和安全性方面的要求,这就是Binder。

二 Binder架构和通信模型

1 架构

我们知道Binder 是基于 C/S 架构的。由一系列的组件组成,包括 ClientServerServiceManagerBinder 驱动。其中 Client、ServerService Manager 运行在用户空间,Binder 驱动运行在内核空间。

其中 Service Manager 和 Binder 驱动由系统提供,而 ClientServer 由应用程序来实现。 ClientServer ServiceManager 均是通过系统调用 openmmap ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。

Client Server ServiceManager Binder 驱动这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server )、客户端( Client )、 DNS 域名服务器( ServiceManager )以及路由器( Binder 驱动)之前的关系。
Client 先去ServiceManager中拿到 Server binder BpBinder ),然后 Client 再通过这个 binder 调用 ”Server 端的代码。当然这个“ 调用 是跨进程的过程,需要通过 ioctl 来支持,也就是需要 Binder 驱动支持。具体的流程如下图所示:

 

2 通信模型 

由上面Binder的结构可知,Binder通信模型和网络通信有异曲同工之妙。网络通信模型如下图:

在角色方面, Client进程就等同于网络请求的Client 端,服务器就是Server进程,路由器相当于Binder驱动,而DNS服务器相当于ServiceManager进程。 Binder 驱动 就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立, Binder 在进程之间的传递, Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

ServiceManager DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了有 IP 地址以外还有自己的网址(域名)。Server 创建了 Binder,并为它起一个可读易记得名字,比如AMSbinder,我们就取了一个名字叫做“activity” ,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“activity”Binder,它位于某个 Server 中。而ServcieManager中就存储了一个表格,这个表格中就有名字和binder引用对应的信息item

Client 获得实名 Binder 的引用
在这个代码 ServiceManager.getService(Context.ACTIVITY_SERVICE) //String ACTIVITY_SERVICE = "activity", 这段代码是Client ServcieManager 中获取 server 进程的 binder 的代码,这个地方获取的是 AMS binder 实体的代码。代码的设计逻辑就是去ServcieManager 的查找表容器中去获取名字为 activity binder 实体。 Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用了。 Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder:
我申请访问名字叫张三的 Binder 引用。 ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应 Binder 引用作为回复发送给发起请求的 Client 。从面向对象的角度看, Server 中的 Binder 实体现在有两个引用: 一个位于ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该 Binder ,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。

总结:

Binder 框架定义了四个角色: Server Client ServiceManager (以后简称 SMgr )以及 Binder 驱动。其中 Server ,Client, SMgr 运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似: Server 是服务器, Client 是客户终端, SMgr 是域名服务器( DNS ),驱动是路由器。
在网络通信中域名服务器的地址是一个固定的地址,所以很方便的通过这个固定地址拿到。那么在 Binder 中, SMgr 是一个进程, Server 是另一个进程, Server SMgr 注册 Binder 必然会涉及进程间通信。
当前实现的是进程间通信却又要用到进程间通信,这就好象蛋可以孵出鸡前提却是要找只鸡来孵蛋。 Binder 的实现比较巧妙:预先创造一只鸡来孵蛋: SMgr 和其它进程同样采用 Binder 通信, SMgr Server 端,有自己的 Binder 对象(实体),其它进程都是Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。
SMgr 提供的 Binder 比较特殊,它没有名字也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR 命令将自己注册成 SMgr Binder 驱动会自动为它创建
Binder 实体(这就是那只预先造好的鸡)。其次这个 Binder 的引用在所有 Client 中都固定为 0 而无须通过其它手段获得。也就是说, 一个Server 若要向 SMgr 注册自己 Binder 就必需通过 0 这个引用号和 SMgr Binder 通信。类比网络通信, 0 号引用就好比域名服务器的地址,你必须预先手工或动态配置好。要注意这里说的 Client 是相对 SMgr 而言的,一个应用程序可能是个提供服务的Server ,但对 SMgr 来说它仍然是个 Client
有了以上的知识铺垫,我们可以比较清晰的把 BInder 通信的流程梳理出来:
1. 首先, 一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager。
2. Server 通过驱动向 ServiceManager 中注册 Binder Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包。
传给 ServiceManager ServiceManger 将其填入查找表。
3. Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用。
4. 通过这个 Binder 实体引用, Client 实现和 Server进程的通信。

3 源码中Binder是如何创建的?

我们已经讲了这么多关于 binder 的通信的案例了,那么 Binder 到底是在什么时候初始化呢?
Binder 初始化一般是指 binder 驱动的初始化,大家在使用 binder 的过程中,我们从来没有执行过 new Binder 的方式来实现Binder 初始化,原因很简单: binder 初始化有它自身独立的特点。
每一个应用进程启动的时候,都是通过 zygote fork 产生的,所以,当 fork 产生进程后 app 进程的代码就开始执行,就开始运行的地方如下
  public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
        //初始化运行环境
        RuntimeInit.commonInit();
        //启动Binder,在androidRuntime.cpp中注册
        ZygoteInit.nativeZygoteInit();
        //通过反射创建程序入口函数的Method对象,并返回Runnable对象
        //ActvityThread.mian()
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
    }
大家可以看到,会执行 ZygoteInit. nativeZygoteInit() 函数,而 nativeZygoteInit 函数执行 appRuntime onZygoteInit 代码,也就是App_main.cpp 中的 onZygoteInit() 函数,函数如下:
 virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }
ProcessState self 函数里面就会初始化 ProcessState (),而这个初始化的一个非常重要的动作就是启动 binder
驱动和并构建 binder Map 映射。具体代码如下:
ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver))//打开binder的虚拟驱动
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mBinderContextCheckFunc(nullptr)
    , mBinderContextUserData(nullptr)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
    , mCallRestriction(CallRestriction::NONE)
{

// TODO(b/139016109): enforce in build system
#if defined(__ANDROID_APEX__)
    LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif

    if (mDriverFD >= 0) {
        //调用mmap接口向Binder驱动中申请空间的内存
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
            close(mDriverFD);
            mDriverFD = -1;
            mDriverName.clear();
        }
    }

#ifdef __ANDROID__
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened.  Terminating.", driver);
#endif
}
所以,总的来说, Binder 的初始化是在进程已创建就完成了。创建进程后会第一时间为这个进程打开一个 binder 驱动,并调用mmap 接口向 Binder 驱动中申请内核空间的内存。

三 AIDL

1 简介

AIDL是Android Interface Definition Language的简写,即Android接口定义语言。AIDL就是Android实现IPC机制的方式之一,是一个Android IPC通信的一个代码规范。是对Binder协议的一个封装,可以更好的使用Binder进行IPC通信,其底层是Binder。

编译器可以通过aidl文件生成一段代码,生成的代码封装了 binder ,可以当成是 binder的延伸。
主要就是将 Binder 封装成一个代理对象proxy ,从用户的角度看,就像是客户端直接调用了服务端的代码。

2 使用方法

由于AIDL的使用非常简单这里通过一个Demo来讲解一下AIDL。

  1.  服务端StudentService中定义IStudentAidlInterface.aidl文件和远程调用的方法。并且将AIDL整个文件拷贝到客户端相应位置(服务端和客户端的aidl文件包名必须保证一致)代码如下:
    // IStudentAidlInterface.aidl
    package com.herrick.studentservice;
    interface IStudentAidlInterface {
    
        void setName(String name);
        String getName();
    
    }
  2.  服务端StudentService中定义StudentService服务,在IStudentAidlInterface.Stub中实现需要远程调用的方法,并在onBind中返回IBinder实例
    package com.herrick.studentservice
    
    import android.app.Service
    import android.content.Intent
    import android.os.IBinder
    import android.os.ParcelFileDescriptor
    import android.util.Log
    
    class StudentService : Service() {
        private val mBinder by lazy { StudentBinder() }
        private var mName: String = ""
        override fun onBind(intent: Intent): IBinder {
    
            return mBinder
        }
    
        private inner class StudentBinder : IStudentAidlInterface.Stub() {
            override fun setName(name: String?) {
                Log.d("StudentService", "setName: $name")
                mName = "$name===Service"
            }
    
            override fun getName(): String {
                return mName
            }
    
        }
    }
  3. 在服务端AndroidManifest. xml 中配置service,android:enabled="true",android:exported="true"和action。同时要在客户端App的AndroidManifest. xml配置对应的权限,注意要保证包名一致,否则服务绑定失败。
    服务端:
    <service
                android:name=".StudentService"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.herrick.studentservice.aidlService" />
                </intent-filter>
            </service>
    客户端权限:
    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <queries>
            <package android:name="com.herrick.studentservice" />
        </queries>
    ...
  4. 在StudentClient端的MainActivity.kt中自定义ServiceConnection,然后将ServiceConnection传入bindService,获取到IBinder后实现远程调用。
    package com.herrick.studentclient
    
    import android.content.ComponentName
    import android.content.Context
    import android.content.Intent
    import android.content.ServiceConnection
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.os.IBinder
    import android.util.Log
    import android.widget.Toast
    import com.herrick.studentclient.databinding.ActivityMainBinding
    import com.herrick.studentservice.IStudentAidlInterface
    
    class MainActivity : AppCompatActivity() {
        private var mStub: IStudentAidlInterface? =null
        private val TAG = "client_MainActivity"
        private lateinit var binding: ActivityMainBinding
        private val serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
                Log.d(TAG, "onServiceConnected: ")
                mStub = IStudentAidlInterface.Stub.asInterface(binder)
                mStub?.apply {
                    name = "赵四"
                    Log.d(TAG, "获取名称: $name")
                }
            }
    
            override fun onServiceDisconnected(componentName: ComponentName?) {
                Log.d(TAG, "onServiceDisconnected: ")
            }
    
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
            binding.btn.setOnClickListener {
                bindService()
            }
            binding.btnCancel.setOnClickListener {
                unbindService(serviceConnection)
            }
        }
    
        override fun onDestroy() {
            super.onDestroy()
            unbindService(serviceConnection)
        }
    
        fun bindService() {
            if (mStub != null) {
                return
            }
            val intent = Intent("com.herrick.studentservice.aidlService")
            intent.setClassName("com.herrick.studentservice","com.herrick.studentservice.StudentService")
            try {
                val bindSucc = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
                if (bindSucc) {
                    Toast.makeText(this, "bind ok", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "bind fail", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
  5. aidl自动生成的文件定义完aidl文件,编译会自动生成一个java接口文件。 这个接口文件在build目录下,具体路径如下:

AIDL生成文件

打开生成的文件代码如下,这里只需要看那些核心代码就可以。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.herrick.studentservice;
public interface IStudentAidlInterface extends android.os.IInterface
{
  /** Default implementation for IStudentAidlInterface. */
  public static class Default implements com.herrick.studentservice.IStudentAidlInterface
  {
    @Override public void setName(java.lang.String name) throws android.os.RemoteException
    {
    }
    @Override public java.lang.String getName() throws android.os.RemoteException
    {
      return null;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.herrick.studentservice.IStudentAidlInterface
  {
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.herrick.studentservice.IStudentAidlInterface interface,
     * generating a proxy if needed.
     */
    public static com.herrick.studentservice.IStudentAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.herrick.studentservice.IStudentAidlInterface))) {
        return ((com.herrick.studentservice.IStudentAidlInterface)iin);
      }
      return new com.herrick.studentservice.IStudentAidlInterface.Stub.Proxy(obj);
    }
    @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;
      if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
        data.enforceInterface(descriptor);
      }
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
      }
      switch (code)
      {
        case TRANSACTION_setName:
        {
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.setName(_arg0);
          reply.writeNoException();
          break;
        }
        case TRANSACTION_getName:
        {
          java.lang.String _result = this.getName();
          reply.writeNoException();
          reply.writeString(_result);
          break;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
      return true;
    }
    private static class Proxy implements com.herrick.studentservice.IStudentAidlInterface
    {
      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;
      }
      @Override public void setName(java.lang.String name) 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(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public java.lang.String getName() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
    }
    static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  }
  public static final java.lang.String DESCRIPTOR = "com.herrick.studentservice.IStudentAidlInterface";
  public void setName(java.lang.String name) throws android.os.RemoteException;
  public java.lang.String getName() throws android.os.RemoteException;
}
Stub 里面的关键函数1-asInterface
   public static com.herrick.studentservice.IStudentAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.herrick.studentservice.IStudentAidlInterface))) {
        return ((com.herrick.studentservice.IStudentAidlInterface)iin);
      }
      return new com.herrick.studentservice.IStudentAidlInterface.Stub.Proxy(obj);//核心代码1
    }

该核心代码就是创建一个Proxy对象,同时将IBinder对象的obj传值给Proxy

Stub类中的关键函数2-onTransact():
    @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;
      if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
        data.enforceInterface(descriptor);
      }
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
      }
      switch (code)
      {
        case TRANSACTION_setName://核心代码2
        {
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.setName(_arg0);
          reply.writeNoException();
          break;
        }
        case TRANSACTION_getName://核心代码2
        {
          java.lang.String _result = this.getName();
          reply.writeNoException();
          reply.writeString(_result);
          break;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
      return true;
    }
onTransact 函数功能主要是接收来自 binder 底层的调用,通过 binder 底层调用 onTransact 函数,将函数执行到核心代码2 ,具体的流程请看后面的小结。
Proxy类里面的关键函数setName和getName
    private static class Proxy implements com.herrick.studentservice.IStudentAidlInterface
    {
      ...
     //核心代码3
      @Override public void setName(java.lang.String name) 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(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
        //核心代码3
      @Override public java.lang.String getName() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
    }
    static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  }
这个函数是和 aidl 文件里面定义的函数名字是一样的,后面进行跨进程通信会引用到这个函数,这个的功能是创建一 个Parcel 数据 _data ,为什么必须是 Parcel 数据了,因为 android 跨进程传递的数据必须序列化,而序列化所采用的方式就是Parcel 。大家如果研究过 Parcel 序列化,你们会发现它的主要实现是在 native 层,而 Binder 通信最主要的部分是在native 层。另外,如果跨进程的调用getName 有返回值,那么这个返回值讲以 _replay 来存储和传递。
以上是 aidl 生成的 java 代码的介绍,其实在 android api 29 之后, aidl 可以生成 C++ 的代码,具体的生成的代码的逻辑和java 代码的逻辑一模一样,只是语言有修改而已。
小结
在运用 AIDL 通信的过程中,首先 Client 端(某个 acitivty )先会发起 bindService 的请求,此时 Server 端(某 service) 会将自己的binder给Client端。bindServcie 会去绑定服务 "RemoteTestService" ,在执行 bindService 的时候会回调到 connection中去。 当绑定成功就会执行 onServiceConnected 回调函数,回调函数就会有一个 IBinder 对象 service ,此时通过 asInterface 函数,会将这个 IBinder 对象( service )转换成为一个 Proxy 对象。所以,我们 Client 去进行跨进程函数调 用的时候就是使用这个 proxy 对象进行。

总结

binder 基于 AIDL 的通信流程图如下:
Client 端: MainActivity
Server 端: Service
Service 的对外 aidl 接口如上面的案例所示,
MainActivity 已经通过 BindService 拿到了 Service IBinder 对象。
aidl 通信的基本步骤如下:
1. Client 通过 ServiceConnection 获取到 Server Binder ,并且封装成一个 Proxy
2. 通过 Proxy 来同步调用 IPC 方法( setName ),同时通过 Parcel 将参数传给 Binder ,最终触发 Binder 的transact方法。
3. Binder transact 方法最终会触发到 Server Stub onTransact 方法。
4. Server Stub onTransact 方法中,会先从 Parcel 中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel 后传回。
5. 请注意: Client Ipc 方法中,执行 Binder transact 时,是阻塞等待的, 一直到 Server 逻辑执行结束后才会继续执行。当然,如果IPC 方法是 oneWay 的方式,那么就是非阻塞的等待。
6. Server 返回结果后, Client Parcel 中取出返回值,于是实现了一次 IPC 调用。
所以, aidl 文件会生成一个 java 文件,这个 java 文件的意义在于将核心的 binder 驱动封装成为 java 层可以直接调用的代码,同时也处理了将java 层的数据格式转换为 Parcel 格式数据进行跨进程传递的一个功能。所以, aidl 是一个使用binder的标准方案,该方案的代码同样的可以通过用户自己编写的方式完成。

4 bindService的流程

之所以在这里分析bindService的流程是为了给大家展示一个app进程和另外一个进程的Service实现跨进程通信的过程,这个过程将给大家全面的展示开发中最常见的跨进程通信时Client端拿到ServerBinder的总流程。

基于android API 30 代码的执行流程如下:

ContextWrapper . bindService ()
ContextImpl . bindService ()
ContextImpl . bindServiceCommon ()
ActivityManagerService . bindService ()
ActiveServices . bindServiceLocked ()
ActiveServices . requestServiceBindingLocked ()
ActivityThread . ApplicationThread . scheduleBindService ()
ActivityThread . sendMessage ()
ActivityThread . handleBindService ()
ActivityManagerService . publishService ()
ActiveService . publishServiceLocked ()
LoadedApk . ServiceDispatcher . InnerConnection . connected ()
LoadedApk . ServiceDispatcher . connected ()
ActivityThread . post ()
LoadedApk . ServiceDispatcher . RunConnection . run ()
LoadedApk . ServiceDispatcher . doConnected ()
ServiceConnection . onServiceConnected ()
上面会经历几个进程:
1 App 进程 A ,发起 bindService();
2 AMS 所在的 systemServer 进程;
3 Service 所在的进程B
4 )还有一个隐藏在背后支撑 binder 通信的 ServiceManager
上面的代码是具体的 bindService 的过程,我们可以梳理出一下的图,通过图片来分析会更加清晰。 具体流程如下:
1 Activity 作为 Client 发起 bindService ,最终会调度到 AMS 去执行 bindService 。在这个过程中, Client 要去调用AMS的代码,所以此时就会涉及到跨进程调度,基于第一章的 Binder 通信模型我们不难知道, Client 会先和 ServiceManager通信,从 ServiceManager 中拿到 AMS IBinder
2 Activity 拿到 AMS IBinder 后,跨进程执行 AMS BindService 函数;
3 )由于 AMS 管理所有的应用进程,因此 AMS 中持有了应用进程的 Binder ,所以此时 AMS 可以发起第 4 步也就是跨进程调度scheduleBindService();
4)Server 端会在收到 AMS bindService 的请求后,会将自己的 IBinder 发送给 client ,但是 Server 必须通过 AMS 才能将Binder 对象传过去,所以此时需要跨进程从 ServiceManager 中去拿到 AMS binder
5 Server 端通过 AMS binder 直接调用 AMS 的代码 publishService(), service Binder 发送给 AMS
6 )经过层层调用,最终 AMS Server 端的 binder 通过回调 connect 函数传递给了 Client 端的 Activity
以上就是 bindService 的全流程,这个流程主要的目的是将 Server 端的 Binder 对象发送给 Client 端。从此以后, Client 端就可以通过Server 端的 binder Server 端像调用自己的代码一样完成跨进程通信了。

四 JavaNative 通信的基本流程

前面 已经基本将 Binder 通信的上层逻辑解释清楚了,接下来的重点是分析 binder 的上层是如何走到 native 层的,换一句话说, native 层如何被封装成为上层的。也就是 Binder Java Native 层的相互转化。
当讲到 Binder java&Native 层的通信,天生最好研究的就是 ServiceManager 类了,因为,它是运用最广泛的也是大家最熟悉的使用Binder 通信的场景。
通过上图,我们不难发现,在 Binder 通信必然需要有 JNI 层的支撑,缺乏 JNI 层,将无法完成 java Native 层的调用,因此我们必须研究一下Java framework 层与 Native 层之间的相互调动。
1 Client 端通过 ServiceManager 拿到 Server 端服务的 Binder 代理,也就是 BinderProxy( Server Binder 的一个代理)
2 )这个 BinderProxy 的访问需要经过 JNI 层的 Android_util_binder 类将请求转交给 native BpBinder p 代表代理的意思);
3 BpBinder 会通过 ioctl 将请求转交给 Binder 驱动设备;
4 )在服务端注册了一个监听请求的回调函数, 一旦驱动层收到 BpBinder 的调用,就会回调 BBInder 注册的回调函数,于是,就将请求转给了BBinder
5 BBinder 拿到请求后,会进行一些数据的处理,然后通过 JNI 将请求转交给了 java 类;
6 java 层会通过 aidl 中的函数将请求发送给 Server 端的实现者,由 Server 端通过 stub 去调用相关的执行代码,并将结果通过类似的路径返回。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值