欢迎大家来到IT世界,在知识的湖畔探索吧!
Binder 是 Android 实现并广泛使用的一种进程间通信(IPC)机制。但是,如果你是一名 Android 应用程序开发人员,您可能很少直接使用 Binder。这是因为Android框架公开的编程接口很好地隐藏了Binder的细节,以至于应用程序开发人员几乎看不到它。然而,如果你打算学习Android系统的内部原理,那么深入了解Binder是至关重要的。 Android 是一个庞大的系统,有很多的组件一起工作,而 Binder 是它们之间的纽带,因此如果没有很好地理解 Binder,你将很难理解 Android 的架构视图。因此,我甚至认为 Binder 是你在深入研究其他子系统之前必须深入学习的第一个系统。
历史
Binder 的历史比 Android 本身还要悠久。 Android 中当前的 Binder 实现是 Be Inc. 的 OpenBinder 项目的后代。但是 Android Binder 代码库是从头开始编写的,而不是基于 OpenBinder。对于将 Binder 移植到 Android 而不是使用 Linux 中其他现有 IPC 机制的动机,没有明确的答案。但根据 2009 年 [LKML: Dianne Hackborn: Re: [PATCH 1/6] staging: android: binder: Remove some funny && usage],这可能是因为 Google 的许多 Android 操作系统工程师都来自 OpenBinder 项目。无论实际的决策过程如何,Binder 都被证明在 Android 中取得了巨大的成功。自 Android 第一个版本以来,Binder 的实现几乎没有变化,并且在 Android 中得到了广泛的使用。
入门
应用程序开发人员只需调用 startActivity 即可启动新的 Activity 实例,这是所有 Android 课程第一节课就学习的内容。但在底层,应用程序需要启动与不同系统服务器的一系列复杂交互才能实现这一点。
ActivityTaskManagerService(ATMS)是管理应用程序活动的核心组件,它运行在一个名为system_server 的特权系统进程中,因此应用程序需要首先使用 Binder 向 ATMS 发送请求。应用程序还需要请求同样在 system_server 中运行的 WindowManagerService(WMS) 为新的 Activity 实例分配一个窗口。此外,应用程序还需要请求运行在自己进程中的 SurfaceFlinger 为新的 Activity 分配一个 Surface。PackageManagerService(PKMS) 和 InputManagerService 在这个 Activity 启动过程中也参与了大量的工作。所有这些步骤都涉及与 Binder 的交互。听起来可能已经很复杂了,但实际上这只涵盖了 Activity 启动过程中与 Binder 交互的一小部分。
在 Android 平台中,Binder 用于核心平台中跨进程发生的所有事情。
进程间通信
Binder 是 Android 实现并广泛使用的一种进程间通信(IPC)机制。
Android 中的进程具有独立的地址空间,进程不能直接访问另一个进程的内存(这称为进程隔离)。出于稳定性和安全原因,这通常是一件好事:多个进程修改同一内存可能会造成灾难性的后果,并且你不希望其他用户启动的潜在恶意进程通过访问邮件客户端的内存来转储你的电子邮件。
然而,如果一个进程想要向其他进程提供一些有用的服务,它需要提供某种机制来允许其他进程发现这些服务并与这些服务交互。这个机制称为 IPC。
Binder实现
如前所述,一个进程无法访问另一个进程的内存。然而,内核可以控制所有进程,因此可以公开启用 IPC 的接口。
在 Binder 中,这个接口就是 [/dev/binder] 设备,它是由 Binder 内核驱动实现的。Binder 驱动程序是框架的中心对象,所有的 IPC 调用都经过它。
但是数据实际上是如何在进程之间传递的呢?
Binder 驱动管理每个进程的部分地址空间。Binder 驱动程序管理的内存块对于进程来说是只读的,所有写入操作均由内核模块执行。当一个进程向另一个进程发送消息时,内核会在目标进程的内存中分配一些空间,并直接从发送进程复制消息数据。然后,它将一条短消息排队到接收进程,告诉它接收到的消息在哪里。最后,接收者可以直接访问该消息(因为它位于自己的内存空间中)。
如上图,两个虚线框代表两个进程–Client进程和Server进程,Client要和Server通信,首先将数据序列化,然后通过 Binder 驱动程序(图中的Socket,pipe就是Linux内核的跨进程通信协议)将数据发送到 Server 端,之后等 Server 端收到消息通知后,将数据反序列化,调用 Server 端逻辑。
到这里,感觉也就是 C/S 逻辑,最多也就是多加了 Binder 驱动管理。简单来说就这些,但要实现这个,就需要 应用层、Framework层(java层和native层)和驱动层协调来完成了。如下图:
AIDL
AIDL(Android Interface Definition Language)是 Android 接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于 IPC 的代码。
语法
AIDL 的语法十分简单,与 Java 语言基本保持一致,需要记住的规则有以下几点:
1. AIDL 文件以 .aidl 为后缀名。
2. AIDL 支持的数据类型分为如下几种:
- 八种基本数据类型:byte、char、short、int、long、float、double、boolean。
- String,CharSequence。
- 实现了 Parcelable 接口的数据类型。
- List 类型。List 承载的数据必须是 AIDL 支持的类型,或者是其它声明的 AIDL 对象。
- Map 类型。Map 承载的数据必须是 AIDL 支持的类型,或者是其它声明的 AIDL 对象。
3. AIDL 文件可以分为两类。一类用来声明实现了 Parcelable 接口的数据类型,以供其他 AIDL 文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向 Tag 就是用来标注这些方法的参数值。
4. 定向 Tag。定向 Tag 表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。此外,如果 AIDL 方法接口的参数值类型是:基本数据类型、String、CharSequence 或者其他 AIDL 文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向 Tag。定向 Tag 具体的使用差别后边会有介绍。
5. 明确导包。在 AIDL 文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下。
命令工具
aidl 自动生成 Java源码命令行工具,在 Android SDK 的 build-tools 目录下。
aidl-cpp 自动生成 C++ 源码命令行工具,这个包含在 Android 开源项目(AOSP)中。
你如果是用 Android Studio 开发,这个命令行已经集成 IDE 了,你可以不用管。但如果是想独立安装使用,可以单独安装。例如 Ubuntu 18.0.4 LTS 系统下:
sudo apt install aidl
欢迎大家来到IT世界,在知识的湖畔探索吧!
就可以安装成功命令行工具了。
命令行使用参数规则如下图(Windows系统):
28.0.3:
34.0.0:
欢迎大家来到IT世界,在知识的湖畔探索吧!(base) PS F:\Android\android-sdk-windows\build-tools\34.0.0> .\aidl --help AIDL Compiler: built for platform SDK version 34 usage: F:\Android\android-sdk-windows\build-tools\34.0.0\aidl.exe --lang={java|cpp|ndk|rust} [OPTION]... INPUT... Generate Java, C++ or Rust files for AIDL file(s). F:\Android\android-sdk-windows\build-tools\34.0.0\aidl.exe --preprocess OUTPUT INPUT... Create an AIDL file having declarations of AIDL file(s). F:\Android\android-sdk-windows\build-tools\34.0.0\aidl.exe --dumpapi --out=DIR INPUT... Dump API signature of AIDL file(s) to DIR. F:\Android\android-sdk-windows\build-tools\34.0.0\aidl.exe --checkapi[={compatible|equal}] OLD_DIR NEW_DIR Check whether NEW_DIR API dump is {compatible|equal} extension of the API dump OLD_DIR. Default: compatible F:\Android\android-sdk-windows\build-tools\34.0.0\aidl.exe --apimapping OUTPUT INPUT... Generate a mapping of declared aidl method signatures to the original line number. e.g.: If line 39 of foo/bar/IFoo.aidl contains: void doFoo(int bar, String baz); Then the result would be: foo.bar.Baz|doFoo|int,String,|void foo/bar/IFoo.aidl:39 F:\Android\android-sdk-windows\build-tools\34.0.0\aidl.exe [OPTION]... INPUT [OUTPUT] Generate a Java file for an AIDL file. OPTION: -I DIR, --include=DIR Use DIR as a search path for import statements. -p FILE, --preprocessed=FILE Include FILE which is created by --preprocess. -d FILE, --dep=FILE Generate dependency file as FILE. Don't use this when there are multiple input files. Use -a then. -o DIR, --out=DIR Use DIR as the base output directory for generated files. -h DIR, --header_out=DIR Generate C++ headers under DIR. -a Generate dependency file next to the output file with the name based on the input file. -b Trigger fail when trying to compile a parcelable declaration. --ninja Generate dependency file in a format ninja understands. --rpc (for Java) whether to generate support for RPC transactions. --structured Whether this interface is defined exclusively in AIDL. It is therefore a candidate for stabilization. --stability=<level> The stability requirement of this interface. --min_sdk_version=<version> Minimum SDK version that the generated code should support. Defaults to 1 for --lang=java, 23 for --lang=cpp, 29 for --lang=ndk, 31 for --lang=rust, -t, --trace Include tracing code for systrace. Note that if either the client or service code is not auto-generated by this tool, that part will not be traced. --transaction_names Generate transaction names. -v VER, --version=VER Set the version of the interface and parcelable to VER. VER must be an interger greater than 0. --hash=HASH Set the interface hash to HASH. --log Information about the transaction, e.g., method name, argument values, execution time, etc., is provided via callback. -Werror Turn warnings into errors. -Wno-error=<warning> Turn the specified warning into a warning even if -Werror is specified. -W<warning> Enable the specified warning. -Wno-<warning> Disable the specified warning. -w Disable all diagnostics. -w wins -Weverything -Weverything Enable all diagnostics. --help Show this help. INPUT: An AIDL file. OUTPUT: Path to the generated Java or C++ source file. This is ignored when -o or --out is specified or the number of the input files are more than one. For Java, if omitted, Java source file is generated at the same place as the input AIDL file, HEADER_DIR: Path to where C++ headers are generated.
可以看出来, API 版本不同,工具也是有差别的。
命令行简单使用:
.\aidl -I.\aidl .\aidl\mobi\huanyuan\samples\binder\api\IOrderInterface.aidl .\generated\mobi\huanyuan\samples\binder\api\IOrderInterface.java
这里需要注意,相关联的类都可以在 .\aidl 这个目录下找到才可以,否则就会报错 Couldn’t find import for class *,如果接口关联有点多,还是比较麻烦的,所以建议还是使用 Android Studio 工具,简单、方便。
到这里,Binder 基本知识就完了,下面我们通过一个简单项目来实际操作下。
项目实践
这个项目是用 Github 上一个开源项目改过来的,项目地址[GitHub – androidonekb/HamKing]。
代码库中有三个模块:客户端、服务器和 api。客户端模块模仿客户,服务器模块模仿汉堡店。api 是个 Library,客户端和服务端是 App,它们都引用了 api 模块,该模块定义了两个应用程序之间的通信协议 — 使用 AIDL 定义。客户端和服务器模块是两个独立的 Apk ,在两个独立的进程中运行。所以为了相互通信,两个应用程序需要使用 Binder 。
项目运行截图如下,具体逻辑会在后续文章中详细说明。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/80852.html