导读:

本文分为三个阶段,

  • 第一阶段,原理概述,力争说人话的基础上,讲明白Binder机制在搞什么,为什么这样搞,以及具体是怎么搞的。
  • 第二阶段,代码层面描述,主要描述了,AIDL、IBinder、Binder、IInterface、Binder Driver是如何设计和实现第一阶段的构想。
  • 第三阶段,实例操作,不使用半自动的AIDL工具,纯手动利用Binder实现一把进程间通信。

相信这一套军体拳下来,应该能薛伟地掌握Binder的相关原理。

P.S. 本文code部分使用了伪代码,包含但不限于kotlin、java和汉字。。。不过应该都能看懂

Binder是Android系统用来实现高效IPC(进程间通信)所搭建的框架。在Android系统中的作用和他的名字一样,是像胶水一样,把系统各个服务进程粘合到了一起。

那么Android系统为什么要重新构建一个IPC框架呢?而不是直接使用Linux提供的IPC方案,类似管道,信号,socket等,首先是安全性的考量,这些Linux 原生IPC方案,都不能直接携带进程的信息,只能依赖上层的协议,比如socket是通过ip和端口来区分不同的进程,让伪造访问的难度降低,而Binder 的设计里是带有uid和pid来标识进程身份的,所以安全性会高很多;其次Binder机制的性能会非常的高,他只需要一次内存的拷贝即可, 而Android系统有很多个重要的服务进程,例如 AMS,PMS,SMgr,进程之间的交互非常的频繁,对IPC的性能有较高的要求, 最简单的例子,启动一个Activity也是需要IPC的,启动过程不再本文的描述范围内,有兴趣的话可以自行查阅资料。

首先为什么要IPC框架:


Android系统是基于linux的, 所以存在一个进程隔离的概念, 每个进程都由于虚拟地址的技术,认为自己独享整个系统, 所以并不能直接访问对方的内存地址,导致进程隔离。

那么物理概念的内存肯定只是大家共用的, 一定是可以访问的, 那么如果想完成这种操作就需要进入到内核空间来完成,此时沉入内核空间的进程被称为进入内核态, 而在用户空间则是用户态
在内核空间可以获得高级别权限,例如访问所有的内存地址。
那么显然我们打通User Space 中的两个进程,肯定需要一个内核模块来完成,所以我们需要一个Binder Driver,作为桥梁来互相访问各自进程的内存。

那么为什么Binder Driver这个外来物可以做为内核空间的驱动而存在呢,这是因为LKM机制,可以让一个可编译的模块作为内核模块进入到内核空间提供服务,所以Android 系统构建了一个Binder Driver 在内核态提供支持,讲了这么多,好像想做什么大概是明白了,但具体是怎么做的呢,还是不清楚,面对这种复杂的问题,就是拆分,把他拆分成一个一个问题逐一解决,Android团队设计的时候估计也是这么想的。
假设有一个服务进程S,一个客户端进程C(Binder机制是个C/S架构,你们应该已经发现了)首先一个问题就扑面而来,客户端C如何调用服务端S提供的服务呢,或者说我作为C如何能执行到S的函数,在说白了,需要个协议之类的东西,Binder Driver 对C提供了一个代理,一个IBinder对象,这个东西在C看来就是S,里面有相同的函数可以提供调用, 当C调用了代理IBinder提供的函数时候,Binder Driver就会让C的线程暂时挂起, 然后他去S那执行真正的函数,执行完毕,就把结果返回给C,唤醒C的线程。感觉好像C调用了S一样。
那么第二个问题就来了,我怎么找到C说的这个S,此时需要一个ServiceManager进程登场了, Server进程会通过Binder Driver向ServiceManager注册自己,好比加入通讯录的感觉, 此时能叫上来名字的话就可以找到本人的电话了。

所以大致想实现的设计是这样的:

首先定义一个接口 IDoSomeThing, 这个接口代表了S能干什么的能力,所以S需要实现这个接口 IDoSomeThingImpl , 并且给出这个实现的实例, 供别人调用。
但Binder Driver也要去S实现另一个接口,代表你要通过驱动来提供给其他进程使用,这个接口叫刚才提到的IBinder,所以这个IDoSomeThing还需要实现IBinder,至此Server进程的工作算是做完了。
那么C进程呢, C进程也得有这个IDoSomeThing的接口, 而且要和S的定义一模一样,虽然没有实现,但是起码知道对方S有什么能力, 当我想调用的时候, 我需要和BinderDriver要一个代理对象,代表S,对吧,这个代理对象的类型,也是IBinder, 这个IBinder 就被设计成代表一种可以使用BinderDriver的支持的能力, 至于是提供S的实现,还是提供给C的代理对象,那么就交给S的实现类和BinderDriver来处理。

至此,技术上的设计完成了,感觉上是可行的,那么我们来通过源码来看Android是怎么把这一套玩转跑通的。

从AIDL开始吧,aidl第一部就是使用aidl的语句定义一个接口,例如IDoSomeThing,里面有一个方法doSomeThing(), 你要把它定义为.aidl文件, 编译一下 gradle会帮你生成这个你定义的java版本 的接口:

interface  IDoSomeThing  : android.os.IInterface{doSomeThing() : Unit;// android.os.IInterface 定义的方法//@Override asBinder(): Binder
}

这个IInterface意思就是你是一个Binder官方认证的接口,你需要实现一个asBinder,来提供一个Binder对象。
但还没完,他还会在你的接口里生成一个内部抽象类Stub,而这个内部抽象类是无比的重要。

interface  IDoSomeThing  : IInterface{class Stub extends Binder : IDoSomeThing  {public fun asInterface ( ibinder:IBinder ): IDoSomeThing  {if(调用我的是自己进程内)//返回自己的实现return ibinder as IDoSomeThing  else 证明是其他的进程来调用返回代理对象return new Stub.Proxy(ibinder);}@Overridefun asBinder:IBinder =  this;fun onTransact(){调用我的doSomeThing函数读一下有没有参数传过来val result = this.doSomeThing()把result结果写回去。}}...
}

这个抽象类,有啥用,首先他也是一个IDoSomeThing ,所以在我们S进程里面,提供的实现类其实是这个Stub 的实现类。

class MyServiceProcess: Service{class MyBinder : Stub{doSomeThing(){//实现doSomeRealThing...}}@Overridefun onBind(intent: Intent):IBinder = new MyBinder();}

这里通过Service 的方法onBind,就把我们的实例提供给了BinderDriver.所有的跨进程通讯Server端都需要一个Service来提供给BinderDriver吗?大家可以扒一扒 ActivityManagerServer、IActivityManager、ActivityManagerNative、ActivityManagerProxy、ActivityManager这几个类的源码,看看AMS的IPC过程。
之前说过了, C和S必须都有接口,所以aidl生成的类,必须在两个进程都有才可以,两个APP的话就复制过去。
Server端先看到这里, 我们再看一下Client端,客户端绑定 远程服务的时候,是调用bindService方法(aidl的具体使用方式不再本文描述内,可以查看官方文档,写的特别好).

bindService(Intent(this, RemoteService.class){ service: IBinder->//onServiceConnected的回调内我的server进程服务代理对象 mService = Stub.asInterface(service);
}

欧,看到了asInterface, 这个方法,特别的简单,如果没有跨进程,就是直接返回sub, 如果跨了进程就返回代理对象,而Proxy这个类,又是stub的子类。

class Stub...{class Proxy:IDoSomeThing {private mRemote: IBinder;constuct(remote : IBinder){mRemote = remote;}@Overridefun asBinder = mRemote;fun doSomeThing(){写参数....val result  =  mRemote.transact(参数);....读结果}}
}

马上可以看到的是,她和stub不同,stub is 一个IBinder, 而Proxy只是持有了IBinder的引用,一个是策略模式,一个是代理模式。
可以看到C端通过bind到远程服务S端,会获得一个IBinder对象,我们用asInterface把这个IBinder传过去,换成S端的代理对象Proxy, Proxy是一个IDoSomeThing,所以当我们调用方法doSomeThing的时候, Proxy使用了Binder的一个方法transact,这就通知BinderDriver来处理一下, BInderDriver会调用server中的stub的实现类中的 onTransact()方法,我们在翻上去看一下, 完整的流程就无比清晰了:

C端进程的一个线程tc1,调用Proxy.doSomeThing, Proxy调用transact通知驱动, tc1挂起,
S端进程的一个线程ts1, 会被驱动回调onTransact,执行doSomeThing的真正实现 doRealSomeThing完成后返回给驱动,驱动在唤醒tc1线程,把结果返回,至此结束。

当然,一个S端可能对应着多个C端, S端是一个线程池维护的,给每个C端分别分配线程, 所以,onTransact可能会被多个线程回调,就会ts1,ts2,ts3等等,如果涉及到共享数据,做好多线程控制吧!

至此Binder机制 Java层的代码基本分析完毕,放一张剽窃来的画的贼屌的图吧,接口可能定义的不一样不过不妨碍感受一下。

终于到最后一步,实战阶段了,我们的目标是: 不使用aidl工具生成, 我们手写代码完成功能:
同学们自己去写吧。。反正我写了这个博客,基本上都是手撸了一遍, 还需要掌握一下Parcel这个类,毕竟跨进程涉及到把对象序列化和反序列化,相信你们也可以的。

Binder机制原理、源码、AIDL,IBinder,Binder,IInterface,BinderDriver,需要的都在这里了相关推荐

  1. Android Binder机制情景源码分析之Binder回调注册和反注册

    我们在日常开发中,经常用到Binder来进行跨进程通信,有个比较常见的场景是向服务端注册Binder回调,比如: IActivityManager中有两个成对的方法,Client端向AMS所在的服务端 ...

  2. binder机制原理分析(一):ServiceManager 进程启动

    binder机制原理分析一共分5个部分,其实省了一点,但是分析到后面都差不多了,以后再补充吧. 1.ServiceManager 进程启动 2.普通Service注册到ServiceManager 3 ...

  3. java类加载机制为什么双亲委派_[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的...

    Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的不过源码其实比较简单,接下来简单介绍一下我们先从启动类说起有一个Launcher类 ...

  4. android 开发零起步学习笔记(二十二):ANDROID应用ACTIVITY、DIALOG、POPWINDOW、TOAST窗口添加机制及源码分析(一)

    原文:http://www.cnblogs.com/shanzei/p/4654817.html 第一部分: ANDROID应用ACTIVITY.DIALOG.POPWINDOW.TOAST窗口添加机 ...

  5. 【原理+源码详细解读】从Transformer到ViT

    文章目录 参考文献 简介 Transformer架构 Position Encoding Self-attention Multi-head Self-attention Masked Multi-H ...

  6. 仿猎豹垃圾清理 实现原理+源码

    仿猎豹垃圾清理(实现原理+源码) 转载请注明出处: 仿猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象 ...

  7. 仿猎豹垃圾清理(实现原理+源码)

    仿猎豹垃圾清理(实现原理+源码) 转载请注明出处: 仿猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象 ...

  8. 仿iOS猎豹垃圾清理(实现原理+源码)

    转载请注明出处: 仿猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象的不太一样.怎么说呢, 听我下文一 ...

  9. JVM类加载机制(ClassLoader)源码解析

    http://blog.csdn.net/chenyi8888/article/details/7066569 其实JVM类加载机制,简单地说就是类管理,也就是我们生成的class文件. 三个步骤:装 ...

  10. 深度分析Java的ClassLoader机制(源码级别)

    转载自 深度分析Java的ClassLoader机制(源码级别) Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取 ...

最新文章

  1. LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List--转换二叉树为双向链表--Java,C++,Python解法
  2. php代码审计之MetInfo5.3盲注
  3. python操作mysql(四)
  4. Webpack入门教程二十九
  5. 针对于多线程概念的理解
  6. 使用python自己搭建一个简单的BP神经网络
  7. 完全卸载mysql数据库图文教程
  8. Atitti.java exp ast java表达式语法ast构造器
  9. PySpark : Structured Streaming
  10. react入门教程案例井字棋(包含改进代码)
  11. 干货 | 携程数据血缘构建及应用
  12. 控制台Tomcat Locahost log输出No Spring WebApplicationIn
  13. idea 提示cannot find declaration to go to 解决方法
  14. BZOJ1776: [Usaco2010 Hol]cowpol 奶牛政坛
  15. EasyCVR实现智慧楼宇道闸控制流程及参考代码分享
  16. jquery的优势是什么?
  17. 关于kali连不上网络
  18. python代码在线回归中怎么运行_手把手教你用Python进行回归(附代码、学习资料)...
  19. 人工智能轨道交通行业周刊-第27期(2022.12.12-12.25)
  20. 北大计算机科学与技术保研率,北京师范大学2019届保研率34.7%,北大、人大、清华外校深造前三...

热门文章

  1. 软件测试计划重点事项,软件测试的重点内容和测试计划
  2. tan55度怎么用计算机算,电脑主板温度55度算不算正常?
  3. Java SE 16新版本特性(中文注释说明)
  4. 控制器-----controller
  5. 智库时代杂志智库时代杂志社智库时代编辑部2022年第44期目录
  6. 【C/C++】数组和链表的区别
  7. Linux部署ElasticSearch搜索引擎步骤
  8. VUE入门+5个小案例
  9. 状态空间模型与卡尔曼滤波
  10. PCL最小二乘法拟合平面