惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
B
Blog
V
V2EX
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
博客园 - 聂微东
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog
J
Java Code Geeks
H
Help Net Security
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
Martin Fowler
Martin Fowler
D
Docker
L
LangChain Blog
人人都是产品经理
人人都是产品经理
爱范儿
爱范儿
WordPress大学
WordPress大学
V
Visual Studio Blog

音视频开发进阶

音视频教程-第三节 音视频教程-第二节 真的,AI 可能就是新时代的信息差 充值 Cursor 之后,工作有了哪些变化?🤔 个人'蒸馏'大模型能做哪些有意思的事情 DeepSeek 大模型在 Mac 上的部署和运行 音视频教程-第一节 【WebRTC 专栏】-- Android 开发集成 WebRTC 库的几种方式 【WebRTC 专栏】-- 在 Mac M1 等系列芯片编译和开发 WebRTC-Android 库 Meta Llama3 大模型在 Mac 上的部署和运行 iOS VideoToolBox 解码 HEVC Open-GOP 视频的问题排查 Flutter 状态管理之 InheritedWidget 使用和分析 用 ChatGPT 回答技术问题怎么样 ? 音视频开发系统入门大致路线 UE 4.27 添加自定义 ShadingModel 用 UE4 虚幻引擎做个捏脸小功能~~ UE4 材质练习 之 凹凸贴图偏移的使用 UE4 材质练习系列基础 OpenGL上下文创建以及共享机制 007 | 播放器系列专栏-解析 MP4 文件读取信息 006 | 播放器系列专栏-在 Mac 上查看 MP4 格式信息 干货 | 快速抽取缩略图是怎么练成的? 关于直播、WebRTC、FFmpeg 的那些事 005 | 播放器系列专栏-在 Windows 上查看 MP4 格式信息 将音视频中的花屏、绿屏、黑屏问题一网打尽 关于音视频里面的解码帧率和渲染帧率 004 | 播放器系列专栏-认识MP4视频(下) 003 | 播放器系列专栏-认识MP4视频(上) 入门或者转行音视频,应该要怎么做? H264视频文件如何缩放分辨率
Android Binder 小结
音视频开发进阶 · 2017-12-22 · via 音视频开发进阶

一个专注音视频领域的小圈子

Binder 是什么?在英文中 Binder 是 粘合剂 的意思,表示将两样东西粘在一起。而在 Android 开发中,Binder 的意思多了去了。不同的角度有着不同的解释。 它既可以是 Android 中实现了 IBinder 接口的一个单纯的类,也可以是 Android 中进程跨进程通信(IPC)的一种方式,还可以看作是工作在内核态的 Linux 驱动 /dev/binder

从 Android 的应用层面来说,Binder 是客户端和服务端进行通信的媒介,当 bindService 的时候,服务端会返回一个包含服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据。

比如,我们在写一个无须跨进程的本地服务 LocalService 时,需要在 Service 定义一个类继承自 Binder ,然后在 onBind() 方法中返回该 Binder 对象。这样在 ServiceConnection 接口的 onServiceConnection 方法中就会收到该 Binder 对象,通过该 Binder 对象就可以执行 LocalService 中的公共方法。

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
       mLocalBinder = (LocalService.LocalBinder) iBinder;
}

那么 onServiceConnection 是如何收到该 Binder 对象的呢?这个就是由 ActivityManagerService 来完成的了。AMS 在启动 Service 的过程中,就会回调 onServiceConnection 方法,其中的 IBinder 参数就是 Service 的 Binder 引用。由于绑定的是无须跨进程的本地服务 LocalService ,那么返回的 Binder 也就是 Service 的 Binder本地对象, 执行的方法也是在主线程中了。

而当 Service 组件是一个跨进程的 RemoteService 时,在 onServiceConnected 方法中再如上代码所示的调用返回的 IBinder 对象,则会报如下的转型错误:

java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.glumes.ipc_binder.service.LocalService$LocalBinder

这是因为 RemoteService 在另外的进程中,而不同的进程内存资源是不能共享的,服务端不能将自己的 Binder 本地对象返回过去,只能返回一个 代理对象 Binder ,客户端 Client 通过这个代理对象 Binder 来调用 RemoteService 的相关方法。这个代理对象的类型就是 BinderProxy

在 onServiceConnection 方法中通过 asInterface 方法来将 Service 返回的代理对象 Binder 转换成代理对象 Proxy,其中,Proxy 持有 Binder 的引用,其变量名为 mRemote

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // ActivityManagerService 在 bindService 的过程中会回调该方法,将 Service 的 Binder 对象传回来
            // 再调用 asInterface 方法根据是否在同一进程进行转换。
            // 如果在同一进程,返回的是本地对象 Binder 
            // 如果不在同一进程,返回的是代理对象 Binder
            mService = IBookManager.Stub.asInterface(iBinder);
}

此时,Client 调用 RemoteService 的方法就是通过跨进程的方式了。而两个进程之间的通信就是通过 Binder 驱动来完成的。这是一个类似 U 型 的结构。Client 和 Server 分别在 U 型 的两头,而 Binder 驱动则在 U 型 的底部连接两者。

盗图一张,如下所示:

binder_ipc

Binder 框架

所以,当进行跨进程通信时,Binder 就可以看作是一种架构,这种架构提供了 服务端接口Binder 驱动客户端接口 三个模块,如下图所示:

binder_kuangjia

服务端接口

从 Java 层面来看,一个 Binder 服务端实际上就是一个 Binder 类的对象,该对象一旦创建,内部就启动一个隐藏线程。该线程接下来会接收 Binder 驱动发送的消息,收到消息后,会执行 Binder 对象中的 onTransact 函数,并按照该函数的参数执行不同的服务代码。因此,要实现一个 Binder 服务,就必须重载 onTransact 方法。

Binder 驱动

任意一个服务端 Binder 对象被创建时,同时会在 Binder 驱动中创建一个 mRemote 对象,该对象的类型也是 Binder 类。客户端要访问远程服务时,都是通过 mRemote 对象。

mRemote 对象的 transact 方法最终会调用一个 nativetransactNative方法,将数据写入 Binder 驱动,并由驱动去完成底层的通信过程。

客户端接口

客户端想要访问远程服务,必须获取远程服务在 Binder 驱动中对应的 mRemote 引用。获得该 mRemote 对象后,就可以调用其 transact 方法,而在 Binder 驱动中,mRemote 对象也重载了 transact 方法,重载内容主要包括以下几项:

  • 以线程间通信的模式,向服务端发送客户端传递过来的参数。
  • 挂起当前线程,当前线程正是客户端线程,并等待服务端线程执行完指定服务函数后通知。
  • 接收到服务端线程的通知,然后继续执行客户端线程,并返回客户端代码区。

而客户端获取远程服务在 Binder 驱动中对应的 mRemote 引用,也是在 onServiceConnection 方法中得到的。

onServiceConnection 的方法原型如下,由系统回调该方法,其中的参数 iBinder 就是 Service 的 Binder 代理对象。

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

而在 onServiceConnection 方法内执行 asInterface 方法,返回的内部代理类 Proxy 。

// asInterface 方法原型为,其实参数 obj 为 onServiceConnected 方法的参数 iBinder 。
public static IBookManager asInterface(IBinder obj){

返回的内部代理类 Proxy 构造方法如下,其函数调用都是通过该 mRemote 对象的 transact 方法来完成的,此 mRemote 对象就是远程服务在 Binder 驱动中对应的 mRemote 引用,它的类型就是 BinderProxy,而 Proxy 类只是对 mRemote 对象组合了一下而已。

   private static class Proxy implements IBookManager{
        private IBinder mRemote ;
        public Proxy(IBinder remote) {
            mRemote = remote;
        }

事实上在跨进程的情况下,我们可以不需要使用 Proxy 对象,直接调用 onServiceConnection 方法中 iBinder 对象的 transact 方法也可以完成跨进程通信。

小结

对上述内容简单的小结一下:

  • Android 中的跨进程通信是一个 U 型的过程,由底层的 Binder 驱动来完成双方通信。
  • 客户端 Client 无法得到 Server 的 Binder 本地对象,onServiceConnected 方法返回的是一个 BinderProxy 类型的 Binder 代理对象。
  • 客户端 Client 通过 Binder 代理对象的 transact 方法调用 Binder 驱动,驱动再调用服务端 Server 的 Binder 本地对象的 onTransact 方法来执行对应的操作。
  • 如果服务端 Server 不需要再向客户端 Client 返回数据,则 IPC 调用结束。如需要返回数据,服务端 Server 再调用 Binder 驱动写入数据,客户端 Client 接收到对应数据,IPC 调用结束。

附《Android艺术开发探索》图: binder_note_art

注意事项:

  • 所有可以在 Binder 中传输的接口都需要继承 IInterface 接口。
  • 客户端通过 Binder 代理对象发起远程请求时,当前线程会被挂起,所以不应该在 UI 线程中执行。
  • 另一进程的服务端 Binder 运行在 Binder 线程池中,无需再异步执行,所以 Binder 方法不管是否耗时都应该采用同步的方式去实现。
  • AIDL 支持的数据类型: * 基本数据类型(int,long,char,boolean,double) * String 和 CharSequence 。 * List:只支持 ArrayList,里面每个元素都必须能够被 AIDL 支持。 * Map:只支持 HashMap,里面每个元素都必须被 AIDL 支持,包括 key 和 value 。 * Parcelable:所有实现了 Parcelable 接口的对象。 * AIDL :所有的 AIDL 接口本身也可以在 AIDL 文件中使用。

代码地址: https://github.com/glumes/BinderLearn

参考

  1. 《Android内核剖析》
  2. 《Android开发艺术探索》
  3. http://www.codeceo.com/article/aidl-android-binder.html
  4. http://weishu.me/2016/01/12/binder-index-for-newer/

原创文章,转载请注明来源:    Android Binder 小结