做过Android开发的同学可能有些体会,入门初期,工作内容主要是实现各式各样的UI界面,以及实现应用的业务逻辑。在这个阶段,我们会逐渐熟悉View系统,逐渐学会实现各种各样的界面以及动画效果。再往后,当我们想更深入的学习android系统,比如学习android四大组件的启动过程、AMS、PMS等等时,都会遇到一个叫做Binder的东西。结合笔者的经验,Binder可以说是深入理解Android系统的重要基础。binder作为android系统进程间通信的机制,贯穿在方方面面。我们平时使用最多的startActivity、startService都是通过binder机制与AMS所在进程进行通信。本文主要对Binder机制的体系结构作简要介绍,相信读者看完后,会对binder有一个总体上的理解与把握。

注:笔者初学binder时,曾经看过一些binder介绍的文章,并且过早的纠结于一些文章中的binder代码细节,感觉非常吃力。本文仅从宏观上对binder机制进行介绍。相信读者先理解了binder的总体结构后,再去深入细节,学习效果会更好。

Binder是什么、能做什么?

Binder是android系统里面的进程间通信机制。androd系统中,不同的app运行在不同的进程中,同一个app的不同组件也可能运行在不同的进程中(androidManifest文件中android:process)。当一个进程想为其它进程提供服务时,就需要通过进程间通信的方式来提供服务。打个比方:我们有一个APP1,里面有个service组件可以提供计算器的服务。当另外一个APP2也想使用APP1里面的service的计算器的服务时,由于不同的APP运行在不同的进程中,所以,APP2是无法直接使用APP1里面的service。由于跨越了进程,只能通过进程间通信机制来完成。

再说的形象点,APP1所在进程有一个对象object1,其中有一个方法method1。 APP2所在的另外一个进程,想使用object1的method1方法。binder可能帮助我们在APP2所在进程拿到一个object1对象的引用,使我们能够像调用本地对象一样,通过object1.method1()直接调用。利用binder,我们可以突破进程的限制,将对象传给其它进程,让其它进程方便调用对象的方法。

为什么用Binder?

理解了binder是什么、能做什么后,大家可能会有疑问:android系统基于Linux,linux本身具有很多的进程间通信方式可供选择,为什么android不使用linux自带的一个进程间通信方式,而新创造了一个binder?是重复造轮子吗?

Linux自带的进程间通信方式有:文件、signal、socket、Pipe、共享内存...,  为什么使用Binder?笔者总结原因有两大方面:

1 历史原因。Binder最早并不是为Android系统而设计的,最开始有一个OpenBinder的东西,用在一个叫做Palm Cobaltw的为内核操作系统上。后来,Palm Cobaltw移植到了Linux系统上,OpenBinder也跟着移植了过来。Google在组建Android开发团队的时候,聘请了一位叫做Dianne Hackborn的工程师,而他就是OpenBinder的核心人员。后面在做android进程间通信时,发现binder很合适,就理所当然的在android系统上使用了Binder。

2 binder自身的一些特点和优势。Binder实现进程间通信时,在安全性和效率方面,都很合适用在android系统中。关于这点,先有个印象即可。

Binder有哪些组成部分?

一个Binder系统由四部分组成:Binder客户端、Binder服务端、Binder驱动、服务登记查询模块

Binder客户端:想要使用服务的进程

Binder服务端:实际提供服务的进程

Binder驱动:我们在客户端先通过Binder拿到一个服务端进程中的一个对象的引用,通过这个引用,直接调用对象的方法获取结果。在这个引用对象执行方法时,它是先将方法调用的请求传给binder驱动;然后binder驱动再将请求传给服务端进程;服务端进程收到请求后,调用服务端“真正”的对象来执行所调用的方法;得出结果后,将结果发给binder驱动;binder驱动再将结果发给我们的客户端;最终,我们在客户端进程的调用就有了返回值。Binder驱动,相当于一个中转者的角色。通过这个中转者的帮忙,我们就可以调用其它进程中的对象。

服务登记查询模块:我们调用其它进程里面的对象时,首先要获取这个对象。这个对象其实代表了另外一个进程能给我们提供什么样的服务(再直接一点,就是:对象中有哪些方法可以让客户端进程调用)。首先服务端进程要在某个地方注册登记一下,告诉系统我有个对象可以公开给其它进程来提供服务。当客户端进程需要这个服务时,就去这个登记的地方通过查询来找到这个对象。

Binder各部分是如何工作的?

下面从客户端进程的角度来看看binder的工作机制

作为客户端进程,仅仅是想使用服务端进程提供的服务而已:
 
但是由于进程的隔离性,Client所在的ProcessA是不能读写Service所在的ProcessB中的内容,但系统内核可以,而Binder驱动就是运行在内核态。Binder驱动帮我们中转请求即可:
 
有了Binder驱动后,对于Client端和Service端,需要额外专门与Binder驱动打交道,为了帮Client和Service端屏蔽掉与Binder驱动打交道这件很low的工作,我们可以分别为Client和Service设计一个代理,让他们只与代理打交道即可,将与Binder驱动打交道的任务放在代理里面:
读者可能要问,如果要自己实现Client端和Service端,通过上面的方式,虽然逻辑上独立出来了两个代理(客户端代理的Proxy、服务端的代理Stub),这两个代理还不是要自己实现?如果还是要自己实现,那分出来又有实际的意义?
AndroidSDK为我们提供了一个AIDL工具。我们只要新建一个AIDL文件,像创建普通interface一样(略有不同)在里面定义接口方法。定义好之后,androidSDK会根据aidl里面的接口定义,自动为我们生成一个java文件,其中就包括客户端代理Proxy和服务端代理Stub,并且自动生成了与Binder驱动通信的代码,是不是很爽。上面的图片中,之所以将客户端代理叫做Proxy,服务端代理叫做Stub,就是因为aidl自动生成的代理中,两个代理的类名就是Proxy和Stub。我们在做的实际工作就只有真正的服务功能逻辑了。
 
再更近一步的想想,对于客户端而言,更理想的状态是,客户端完全不用知道调用的对象是一个本地对象,还是一个服务端进程中的对象。按照上面的图片,客户端显然知道自己调用的是远程对象,所以通过一个Proxy来调。我们在开发android应用程序的过程中,都会用到一些系统提供的XXXManager(ActivityManager、PackageManager、WifiManager、PowerManager等等),而这些manager所要实现的目的之一,正是帮Client屏蔽掉Binder的实现细节。有了这些Manager,Client在使用时,首先通过getServiceManager(xxx)获取一个manager对象,后面就直接调用manager中封装好的服务方法即可。这样以来,manager内部实际上还是通过客户端代理,通过binder驱动来跟运行在另外一个进程中的xxxService来通信,但对于Client来说,完全不用知道。如下图:
对于一些系统服务,ActivityManager、PackagerManager等等,不光为Client屏幕了Binder相关细节,还可以对真正的服务端对象提供的服务API进行过滤和控制,可以只为Client暴露一个服务API的子集。
 
最后一个疑问:到上面的图片所讲的内容为止,前提条件是Client先获取到了一个Proxy或者Manager,然后才完成后面的进程间通信。那到底Client是如何获取到Manager或者Proxy呢?
首先以一些系统服务为例:
前面提到,Binder机制的四个组成部分中,有一个是“服务登记查询模块”,对应上图中的Context Manager,在Android系统中运行时的进程名为:servicemanager。在所有服务进程中,servicemanager第一个启动,原因也不难理解,如果其它服务进程先于servicemanager启动,到哪里去注册?
 
如上图所示,假设Service所在的进程ProcessB在servicemanager进程启动后启动,ProcessB需要向servicemanager注册。“注册”这个动作,本质也是要跨进程通信,从ProcessB到servicemanager,这时,Service成为了本次跨进程通信的客户端,ContextManager成为了服务端。客户端到服务端的通信,按前面讲到的内容,需要一个Proxy或者Manager。这个Proxy或者Manager从哪里获取呢?问题好像陷入了循环死锁?因为ContextManager的特殊地位,读者可以理解为系统对它有点特殊待遇,Service可以从一个全局固定的地方直接去拿Proxy或者Manager,而不用像Client那样通过查询来获取Proxy/Manager。拿到了客户端代理,具体注册的通信过程如上文所讲,不再重复。到这里为止,Service就成功注册到了ContextManager中。
 
客户端Client初始化Manager时,manager也像刚才的Service,直接从某个固定的地方拿到ContextManager的代理,通过代理,去查询ContextManager,获取一个“提供所要服务的代理对象”。manager有了这个对象,Client就可以通过manager来使用服务了,调用逻辑如图:
 
当自己来实现Client和Service时,Client如何获取Proxy代理对象?
先讲一下实现方式:一般我们会在App中通过Service组件的方式来创建一个服务。其它App通过BindService的方式连接到Service,通过onServiceConnected回调就能获取Proxy代理对象。具体实现可参考:http://blog.csdn.net/singwhatiwanna/article/details/17041691
 

总结

本文简要介绍了Binder跨进程通信机制的逻辑,希望能帮助初学Binder的同学快速入门,提交学习效率。文中使用的图片以及讲解思路,来自于这个文档:

http://events.linuxfoundation.org/images/stories/slides/abs2013_gargentas.pdf,加进去一些笔者自己的思考和总结。一些地方如果有错误,欢迎指正交流。

另外推荐一篇binder入门的文章:http://weishu.me/2016/01/12/binder-index-for-newer/

Android Binder机制介绍的更多相关文章

  1. android binder机制之——(创建binder服务)

      Binder机制编程 前面的几篇文章具体介绍了android中binder机制的方方面面,相信你对binder机制已经有了较深刻的理解.俗话说得好"学以致用",以下我们就通过在 ...

  2. 【转】Android - Binder机制

    以下几篇文章是分析binder机制里讲得还算清楚的 目录 1. Android - Binder机制 - ServiceManager 2. Android - Binder机制 - 普通servic ...

  3. Android Binder机制彻底梳理二

    根据AIDL了解整体调用流程[重点分析AIDL流程]: 在上一次https://www.cnblogs.com/webor2006/p/11741743.html中我们已经对Android Binde ...

  4. Android Binder机制简单了解

    Binder -- 一种进程间通信(IPC)机制, 基于OpenBinder来实现 毫无疑问, 老罗的文章是不得不看的 Android进程间通信(IPC)机制Binder简要介绍和学习计划 浅谈Ser ...

  5. ANDROID BINDER机制浅析

    Binder是Android上一种IPC机制,重要且较难理解.由于Linux上标准IPC在灵活和可靠性存在一定不足,Google基于OpenBinder的设计和构想实现了Binder. 本文只简单介绍 ...

  6. Android Binder机制(一) Binder的设计和框架

    这是关于Android中Binder机制的一系列纯技术贴.花了一个多礼拜的时间,才终于将其整理完毕.行文于此,以做记录:也是将自己所得与大家分享.和以往一样,介绍Binder时,先讲解框架,然后再从设 ...

  7. Android Binder机制彻底梳理一

    Binder架构图: 先来瞅一下它的整体架构图: 其中粉红部分是上层的Binder,而蓝色的则是下层的Binder,很显然上层的是依赖于下层的. 什么是Binder[有个大概了解]? 这里从几个层面来 ...

  8. 理解 Android Binder 机制(一):驱动篇

    Binder的实现是比较复杂的,想要完全弄明白是怎么一回事,并不是一件容易的事情. 这里面牵涉到好几个层次,每一层都有一些模块和机制需要理解.这部分内容预计会分为三篇文章来讲解.本文是第一篇,首先会对 ...

  9. 浅谈android binder机制

    binder机制 是谷歌优化在android上更适合终端的IPC(多进程通信方式),满足系统对通信方式,传输性能和安全性的要求. 特性: 1. 用驱动程序来推进进程间的通信.2. 通过共享内存来提高性 ...

随机推荐

  1. C和C++中的引用传递

    两种引用传递的定义方式 第一种 #include<stdio.h> void changeValue(int *a); int main(){ int a =1; changeValue( ...

  2. 使用 EW 作Socks5代理

    简介: EarthWorm是一款用于开启 SOCKS v5 代理服务的工具,基于标准 C 开发,可提供多平台间的转接通讯,用于复杂网络环境下的数据转发. 主页: http://rootkiter.co ...

  3. 【Linux】【自学笔记】Linux下面docker安装mysql

    写在前面: 捣腾继续,之前把一个SpringBoot的程序安装在docker上面,参考链接:https://www.cnblogs.com/aki-stones/p/2019-11-01-note.h ...

  4. MinIO 搭建

    MinIO 搭建 MinIO 是一个基于 Apache License v2.0 开源协议的对象存储服务.它兼容亚马逊 S3 云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片.视频.日志文 ...

  5. LNMP+Redis

    如果要让php支持redis需要安装php-redis模块.可以再github上下载哦. https://github.com/phpredis/phpredis 配置lnmp环境,太简单了就不演示了 ...

  6. zookeeper集群搭建2.7

    http://blog.csdn.net/uq_jin/article/details/51513307

  7. 原生JS实现集合结构

    1. 前言 集合是由一组无序且唯一(即不能重复)的项组成的.你可以把集合想象成一个既没有重复元素,也没有顺序概念的数组.在ES6中已经内置了集合这一数据结构--Set.接下来,我们就用原生JS来实现这 ...

  8. DAY 4 基础算法

    基础算法 本来今天是要讲枚举暴力还有什么的,没想到老师就说句那种题目就猪国杀,还说只是难打,不是难.... STL(一)set 感觉今天讲了好多,set,单调栈,单调队列,单调栈和单调队列保证了序列的 ...

  9. 安全路径——最短路径树+dsu缩边

    题目描述 思路 首先想到$dijkstra$跑完之后$build$一棵最短路径树.要找到每个节点i到根的满足要求的最短路,考虑把一些非树边加进去. 对于非树边$(u,v)$,因为节点i上方的边被占领, ...

  10. StringBuffer 和 StringBuilde

    String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简要的说, String 类型和 StringBuffer 类型的主要性能 ...