Android跨进程通信上

Framework

Android系统分成三层。最上层是application应用层,第二层是Framework层,第三层是native层、Android中的应用层和系统服务层不在同一个进程,系统服务在单独的进程中。 2、Android中不同应用属于不同的进程中 Android应用和系统services运行在不同进程中是为了安全,稳定,以及内存管理的原因,但是应用和系统服务需要通信和分享数据。

优点 安全性:每个进程都单独运行的,可以保证应用层对系统层的隔离 稳定性:如果某个进程崩溃了不会导致其他进程崩溃。内存分配:如果某个进程已经不需要了可以从内存中移除,并且回收相应的内存

程序中的多进程

虚拟机分配给各个进程的运行内存是有限制的,LMK也会优先回收对系统资源的占用多的进程。

多进程的好处

  1. 突破进程内存限制,如图库占用内存过多,因为操作系统分配内存的时候是按照进程来分配的;
  2. 功能稳定性:独立的通信进程保持长连接的稳定性;
  3. 规避系统内存泄漏: 独立的WebView进程阻隔内存泄露导致的问题:
  4. 隔离风险:对于不稳定的功能放入独立进程,避免导致主进程崩溃

比如腾讯的包里边,就会有很多进程同时在运行。

Binder的出现

出身

George Hoffman当时任Be公司的工程师,他启动了一个名为OpenBinder的项目,在Be公司被ParmSource公司收购后,OpenBinder由Dinnie Hackborn继续开发,后来成为管理ParmOS6 CobaltOS的进程的基础。在Hackborn加入谷歌后,他在OpenBinder的基础上开发出了Android Binder(以下简称Binder),用来完成Androi的进程通信。

为什么要学习binder

  1. startActivity的时候,会获取AMS服务,调用AMS服务的startActivity方法
  2. startActivity传递的对象为什么需要序列化
  3. bindService为什么回调的是一个lbinder对象
  4. 多进程应用,各个进程之间如何通信AIDL的使用

Binder是什么?

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

  • 机制:Binder是一种进程间通信机制;
  • 驱动:Binder是一个虚拟物理设备驱动;
  • 应用层:Binder是一个能发起通信Java类:
  • Framework/Native:Binder连接了Client、Server、Service Manager和Binder驱动程序,形成一套C/s的通信架构。

为什么选择Binder?

首先我们需要了解安卓中有哪些跨进程通信的方式:

  1. 共享内存:WS与SurfaceFlinger通信的时候,如果有大块的数据需要交给SF去渲染的时候,就会用到。安卓的Ashmem机制也是利用的共享内存。较大的 bitmap 直接通过 Intent 传递容易抛异常是因为 Intent 启动组件时,系统禁掉了文件描述符 fd 机制 , bitmap 无法利用共享内存,只能拷贝到 Binder 映射的缓冲区,导致缓冲区超限, 触发异常; 而通过 putBinder 的方式,避免了 Intent 禁用描述符的影响,bitmap 写 parcel 时的 allowFds 默认是 true , 可以利用共享内存,所以能高效传输图片。
  2. 管道:子进程与父进程通信。
  3. 消息队列。
  4. Socket:AMS与zygote通信。
  5. binder。 这些通信方式都需要经过内核空间。 Server与Client通过Binder通信的时候,会为每一个Client的通信渠道创建一个线程。这也就是Binder线程池的由来。

共享内存

  • 优点:速度快,性能最好;
  • 缺点:没有同步控制(两个进程同时改变内存,存在互斥访问问题),容易访问紊乱,信息安全无保障。

管道

也存在于内核空间,管道分为命名管道,和无名管道。这两者都需要一个介质来作为管道,而我们往往会用一种特殊的文件来做为管道文件。两个进程通信的时候,会通过写段 写入数据,然后再读段 中读取数据。数据需要经过两次复制,一次是从进程A到管道,另一次是从管道到进程B,反之亦然。管道的读写分为阻塞和非阻塞的。为了提升管道的读写速度,一般的,也会有缓冲区,缓冲区一般也会有大小的限制。如果传输的数据一旦超过了缓冲的大小,或者在阻塞模式下没有安排好管道的读写。都会出现阻塞情况。另外管道上边传递的数据是无格式的字节数字节流。这就要求管道的写入方和读出方约定好数据的格式。

消息队列

消息队列是存在在内核中的一个链表。允许多个进程同时读写消息。发送方与接收方要同时约好消息体的数据类型以及大小。

Socket

由网络通信演变而来,典型的CS模式。需要两次拷贝。Client->内核->Server。

Binder

采用的CS结构。binder通信得益于mmap,内存映射。主要实现方式是将读缓冲区的地址和用户缓冲区的地址进行映射,内核缓冲区和应用缓冲区共享,从而减少了从读缓冲区到用户缓冲区的一次CPU拷贝。 Binder在数据传输的过程中需要一个UID,我们可以通过控制这个来实现用户权限管理。Binder的通信数据大小是1M-8k,有这个上限是因为启动组件时,系统禁掉了文件描述符 fd 机制 , bitmap 无法利用共享内存,只能拷贝到 Binder 映射的缓冲区,导致缓冲区超限, 触发异常; 而通过 putBinder 的方式,避免了 Intent 禁用描述符的影响,bitmap 写 parcel 时的 allowFds 默认是 true , 可以利用共享内存,所以能高效传输图片。

进程通信方式对比

Binder通信的基本模型

内存划分

内存被操作系统划分成两块:用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

binder是如何管理的

上图中,ioctl就是系统调用。Client端要与服务端通信,首先必须要拿到Servier端Binder。一般情况下,Server端的Binder会先启动。然后将自己的Binder注册到ServiceManager里边,ServiceManager里边维持了一个表,这个表里边包含了Server的binder。Client直接通过ServiceManager获取到对应的Binder。然后利用这个Binder进行跨进程通信。例如AMS,就是自己把自己注册到SM中的。不过有个问题,ServiceManager也是快进程的通信,我们通过ServiceManager去获取AMS的时候,怎么拿到ServiceManager的Binder对象呢?这是因为ServiceManager的底层handle节点固定了是0,就像DNS服务器一样,他的IP固定为8.8.8.8。 我们在拿到Binder驱动之后,然后再利用Binder驱动来完成真正的跨进程通信。

Binder是什么时候创建的?

为什么Binder需要在App启动时第一时间创建。这是因为进程启动之后会启动Activity等组件,需要跨进程通信。

跨进程通信模型

binder驱动属于Server端,在内核态。我们平常所写的代码是java代码,那么如果通过我们的java代码调用到内核态的驱动呢?这个就涉及到java native的通信,因此我们就产生了AIDL,这个AIDL可以生成java文件,以及C++文件,方便我们利用Binder驱动。

下边就是Android 快进程通信模型

stub就是一个服务端的代理,使用了统一代码进行实现,是服务端能够提供哪些服务的一个存根 减少了用户的封装过程。 proxy 服务端能够提供哪些服务的一个代理,只是这个是给客户端用的,代理的是Binder驱动。因为Client需要通过这个代理来访问Service,而不能直接通过Stub去访问,

当外部需要调用的时候,将stub类交给对应的调用者,这是因为跨进程是不能直接访问真实的Service对象的,只能访问代理对象。这里的这个new ILeoAidl.Stub()实际上就相当于下图的Binder。 之后调用onBind函数就能够真正的拿到服务端的代理对象了。

所以外部要调用我们的代码,就必须要要使用这个stub的继承类来实现,因为只有这个实现类实现了跨进程通信的基础代码编写。

然后客户端要使用的时候,需要bindService,这里我们需要通过Action和package去确定唯一的服务器的binder对象。

这个connection就是与服务端建立连接。内部的两个回调就是与服务器建立状态的回调,这里的service其实就是服务端的Binder对象。这个IBinder只是一个接口,还需要再后边转变成具体的对应的对象才能完成请求。所以这也就是为什么我们需要把服务器的aidl文件包括路径都要复制到client端。

IMyAidlInterface.Stub.asInterface(service)这个代码完成了Server端的代理转变成服务端的一个代理的流程。但是最后的请求还是由代理类发出的。

一个服务注册的一个完整流程

在Android29之后,Android aidl支持生成C++,命令是(参考): blog.csdn.net/yinminsumen...

相关推荐
qiyi.sky2 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~6 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常15 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n042 分钟前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
zqx_72 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己2 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称3 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色3 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2343 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js