android 进程间通信原理

简介: 前言每个Android进程只能运行在自己拥有的虚拟地址空间,对于用户空间。不同进程之间彼此是不能共享的,而内核空间是可以共享的。Client和Server进程通信就是利用进程间可以共享内核内存空间来完成底层通信工作的,Client和Server通过ioctl等和内核空间进行交互。

前言

每个Android进程只能运行在自己拥有的虚拟地址空间,对于用户空间。不同进程之间彼此是不能共享的,而内核空间是可以共享的。Client和Server进程通信就是利用进程间可以共享内核内存空间来完成底层通信工作的,Client和Server通过ioctl等和内核空间进行交互。

img_2ce1f101ce30ad53f419410d3ac13485.png
进程通信架构

1、android的IPC和RPC

RPC指的是跨进程远程调用,强调了调用的功能,即一个进程之间调用另外一个进程的方法。

IPC指的是进程间通信,android使用Binder机制来进行进程间的通信,没有调用的功能。

Android系统的RPC = Binder进程间通信+在Binder基础上建立起来的进程间函数调用机制。

2、android系统的RPC实现

img_5de75a185a283e10232b7ad72baab75d.png
RPC架构图

Android的RPC主要包含Client、Server和ServiceManager。android中使用ServiceManager来管理所有的所有的Server。ServiceManger启动后首先告诉Binder驱动,将自己标识为ServiceManager。在创建一个Server后,首先通过addService将自己交给ServiceMager管理,Client在需要调用Server时直接通过getService到ServiceManager中查找对应的Server,然后调用Server的方法。

下图给出binder在android中的整体架构,从framework到native再到kernel:

img_5ab31809f97999361686995411b41178.png
Binder整体架构


图中红色部分代表整个framework层binder架构的相关组件,Binder类代码Server端,BinderProxy代表客户端。蓝色代码Native层的Binder架构组件。上层framewoek的binder逻辑建立在native层架构的基础之上,核心逻辑都是交给native处理的。Framework的ServiceManager与native的ServiceManager并不完全对应,framework层的ServiceManager类的实现最终是通过BinderProcy传递给native层来完成的。

Server启动后会开启一个线程不停的读取Binder驱动的读接口,这是一个阻塞调用;在需要响应客户端的时候,会调用Binder驱动的写接口进行数据返回。

Client启动后会不停读取Binder驱动的读接口并阻塞;在调用Service时会开启线程调用Binder的写接口;服务器端处理完后调用写接口、唤醒阻塞中的客户端。

所有的通信都是通过底层的Binder驱动实现的。

3、RPC机制java层代码分析

img_f07f84dbf152ee95cedbceabe8045dc4.png
类图


一般我们使用如下方式来获取系统的Service,例如AlarmManager:

AlarmManager wm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);该代码的运行时序如下所示:

img_650b9c6f3ce88ed796e87d05724ec155.png
getSystemService时序

客户端通过Context.getSystemService获取远程服务时,会转到ContextImpl中调用,ContextImpl有一个ServiceFetcher内部类,通过名字知道该类用于获取Service;ContextImpl里面有个static的WALLPAPER_FETCHER,在APK启动时会加载里面的函数,如下所示,其中registerService会将每个Service对应的构造器ServiceFetcher放入SYSTEM_SERVICE_MAP中。

img_7b4460a8643292cd891bd57740aa84a7.png
创建ServiceFetcher

在调用ContextImpl.getSystemService()时,会调用SYSTEM_SERVICE_MAP对应ServiceFetcher的getService()方法,如下所示。ContextImpl有个全局mServiceCache用于缓存用户创建的Service缓存,这样用户再次获取的时候可以直接从缓存取出,避免再次创建。

img_00949662e356ac0642909a6df5ba69ce.png
SeviceFetcher.getService

如果mServiceCache没有需要的Service缓存,则调用ServiceFetcher的createService进行创建,这里就开始和ServiceManager打交道了,以AlarmManager为例,首先调用ServiceManager.getService()获取IBinder,然后调用IAlarmManager.stub.asInterface将该binder转化成客户端可以直接调用的接口,最后将该接口封装成AlarmManager给客户端使用。

img_4f595c34fe17ef95fd61a66c85037a94.png
registerService

我们看到这里调用了ServiceManagerNative.asInterface获取应IServiceManager实例,传入了BinderInternal.getContextObject(),如下所示:

img_66cbb83c8f24c8cb810d447706129b55.png
BinderInternal.getContextObject

getContextObject方法是一个JNI方法,其实sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());就相当于:sServiceManager = ServiceManagerNative.asInterface(new BinderProxy());

接下来就是调用ServiceManagerNative的asInterface函数了.

img_d67e9ad27c7b48dcb3bf9427d722aa2d.png
ServiceManagerNative.asInterface()

这里的参数obj是一个BinderProxy对象,ServiceManagerProxy提供了addService、getService的实现,也就是ServiceManager最终关联到了ServiceManagerProxy上。到这里,在java层我们已经拥有了ServiceManager的远程接口ServiceManagerProxy,对ServiceManager的所有操作将转接到ServiceManagerProxy中。

如此就实现了对android系统的ServiceManager的RPC调用。那么android系统中提供的那些Service是怎样添加到ServiceManager中去的呢?答案就在SystemServer.java类中,SystemServer伴随系统一起启动,之后会运行ServerThread线程。

img_00b6cd3ecd957400e40457318b47fdd0.png
SystemServer.init2()

ServerThread的run方法会完成所有系统Service的创建,并添加到ServiceManager中去,如下所示:

img_d07f9cb87137d97766bd432937492666.png
添加所有的系统Service

其中addService也是调用的ServiceManagerProxy的addService,这样在系统启动后,相当于在OS层维护了一群系统Service的list。

4、几个概念

IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象(Server端)以及Binder代理对象(Client获取的Proxy)的转换。

IInterface代表的就是远程server对象具有什么能力, 表示client与server端的调用契约。具体来说,就是aidl里面的接口。

Java层的Binder类,代表的其实就是Binder本地对象。BinderProxy类是Binder类的一个内部类,它代表远程进程的Binder对象的本地代理;这两个类都继承自IBinder,因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。

在使用AIDL的时候,编译工具会给我们生成一个Stub的静态内部类;这个类继承了Binder,说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力;Stub是一个抽象类,具体的IInterface的相关实现需要我们手动完成,这里使用了策略模式。

目录
相关文章
|
3月前
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
184 8
|
1月前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
124 56
|
2月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
51 3
|
3月前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
51 2
|
3月前
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
299 12
|
2月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
41 0
|
3月前
|
Java Android开发 数据安全/隐私保护
Android中多进程通信有几种方式?需要注意哪些问题?
本文介绍了Android中的多进程通信(IPC),探讨了IPC的重要性及其实现方式,如Intent、Binder、AIDL等,并通过一个使用Binder机制的示例详细说明了其实现过程。
397 4
|
3月前
|
算法 调度 UED
操作系统中的进程管理:原理与实践
在数字世界的心脏跳动着无数进程,它们如同细胞一般构成了操作系统的生命体。本文将深入探讨进程管理的奥秘,从进程的诞生到成长,再到最终的消亡,揭示操作系统如何协调这些看似杂乱无章却又井然有序的活动。通过浅显易懂的语言和直观的比喻,我们将一起探索进程调度的策略、同步机制的重要性以及死锁问题的解决之道。准备好跟随我们的脚步,一起走进操作系统的微观世界,解锁进程管理的秘密吧!
78 6
|
4月前
|
API Android开发
Android P 性能优化:创建APP进程白名单,杀死白名单之外的进程
本文介绍了在Android P系统中通过创建应用进程白名单并杀死白名单之外的进程来优化性能的方法,包括设置权限、获取运行中的APP列表、配置白名单以及在应用启动时杀死非白名单进程的代码实现。
75 1
|
4月前
|
Linux API C语言
Linux源码阅读笔记02-进程原理及系统调用
Linux源码阅读笔记02-进程原理及系统调用

相关实验场景

更多