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...

相关推荐
却尘31 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare32 分钟前
浅浅看一下设计模式
前端
Lee川35 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空1 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust