android aidl及binder基础知识总结

news2025/1/11 2:57:16

1、什么是binder

binder是android framework提供的,用于跨进程方法调用的机制,具有安全高效等特点。

我们知道,在 Android 系统中,每个应用程序都运行在一个独立的进程中,各个进程之间需要进行数据交换和调用,以实现各种功能。为了实现这个目的,binder应运而生。

2、binder的历史

android基于linux内核,linux提供了非常多的跨进程机制。
为什么android要使用binder而不使用linux已有的ipc机制呢,这个抉择在android内部团队中有过争论。 部分工程师认为binder内部的线程等待带来了系统开销,同时没有显示出更好的用处。
但最后binder还是凭借其优势胜出了。 其实,binder是部分android工程师在PalmSource工作时开源的一种ipc机制,负责开发ipc的工程师直接在此binder上进行了移植和开发。

在《Android传奇》一书中有介绍binder的相关历史。

3、binder基本使用

binder是一种架构,这种架构提供了服务端接口、binder驱动和客户端三个模块。

客户端通过binder远程调用服务端接口,binder驱动负责完成这次远程调用。
在android framework中提供了Binder类,一个类如果扩展Binder类,那么该类就有提供远程服务的能力,该类对象一旦创建,其内部就会创建一个隐藏的线程,用来接收binder驱动发送的消息,从而调用Binder类的onTransact()方法。

binder驱动从表现上看,就是在客户端和服务端传递各种消息,以完成跨进程调用。
在任意一个服务端binder对象创建时,同时也会在binder驱动中创建一个对应的mRemote对象,该对象也是binder类型。 客户端就是通过mRemote对象来访问远程服务的,相当于经过了一层代理。 这种机制提供了更好的安全性。

客户端通过获取远程服务在binder驱动中对应的mRemote对象,通过调用mRemote.transact方法从而实现对远程服务的调用。

从整个架构上看,就是服务端提供binder,binder驱动负责转发消息,客户端获取binder引用并调用。 粗略看很简单,细节是魔鬼。

摘抄一个书中的例子,从demo理解binder的调用过程。
比如要提供一个音乐播放的远程调用, 可以提供一个MusicPlayerService,代码如下:

public class MusicPlayerService extends Binder {
    
    public void startPlay(String filePath) {
    
    }
    
    public void stop() {
    
    }
    
    @Override
    public void onTransact(int code, Parcel data, Parcel reply, int flag) {
        // 这个方法很重要,由binder驱动调用
        // 根据约定的code值分别调用不同的业务方法,此处是startPlay和stop
        if (code == 0x100) {
            this.startPlay(file_path)
        } else if (code == 0x101) {
            this.stop()
        }
    }
}

如上MusicPlayerService就可以提供了远程调用服务了。
但是客户端如何使用呢。通过binder架构知道,要想远程调用,必须获取远程服务在binder驱动中对应的binder代理对象。此处假设已经获取到相关的引用mRemote,那么客户端便可以如下调用。

IBinder mRemote = null;
String filePath = "xxx";
int code = 0x100;

Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain(); // 接收远程调用的结果
data.writeString(filePath);
mRemote.transact(code, data, reply, 0)

如上便完成了客户端对服务端的远程调用。 从这个demo就可以看出跨进程调用其实就是要通过Binder驱动来中转一下调用。 以前没看过这个示例时老是不理解aidl生成的规则,不断的复习也总是记不住规则。但是通过这个示例一下就明白了这个过程,在理解的基础上再去手动编写binder类就水到渠成了(虽然一般也用不到手动去写binder类)。

现在就只剩如何获取远程服务对应的binder对象了。 为了简化这个过程,android frameworks通过service提供这个能力。
在service的生命周期方法中有一个onBind方法返回IBinder对象,方法签名如下:

override fun onBind(intent: Intent?): IBinder? {

在调用context.bindService方法进行绑定时,要求传入一个ServiceConnection的对象,在服务绑定成功时会回调onServiceConnection方法,一并传回服务端的Binder代理对象。 如下:

Intent it = new Intent(this, xxxService.class);
context.bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 此处就拿到了远程服务对应的binder了,客户端通过此binder就可以调用远程服务的方法了。 
       
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        
    }
}, Service.BIND_AUTO_CREATE);

仔细想想,应用开发好像也只有这么一条路获取服务端的binder。
但是最初我们定义的MusicPlayerService并不是一个真正的service,无法使用bindService方法,故需要改造一下,让MusicPlayerService继承Service类。 如下:

public class MusicPlayerService extends Service {
       
    private XXXBinder mBinder;
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;  // 此处返回服务端的binder
    }
    
    public void startPlay(String filePath) {
    
    }
    
    public void stop() {
    
    }
   
    // 由于android应用开发上只能通过service进行远程调用,故在service内部类创建binder,方便调用service定义的业务方法。 
    public xxxBinder extends Binder {
        @Override
        public void onTransact(int code, Parcel data, Parcel reply, int flag) {
            // 这个方法很重要,由binder驱动调用
            // 根据约定的code值分别调用不同的业务方法,此处是startPlay和stop
            if (code == 0x100) {
                startPlay(file_path)
            } else if (code == 0x101) {
                stop()
            }
        }
    }
}

由于java不能多继承,继承了service类就不能继承Binder类了。 那么可以在MusicPlayerService类内部构建一个内部类,继承binder,并通过onBind返回binder对象给客户端。 这个代码架构便是使用binder的基本雏形了。但是,在日常开发过程中,为了扩展性和解耦,一般将业务方法通过接口抽象出来。 如下抽象出播放接口。

public interface IPlayer {
    public void startPlay(String filePath);
    public void stop();
}

通过上面的代码可以发现,播放的实现放在XXXBinder类或者MusicPlayerService都是可行的。 为了方便binder调用业务方法,尝试用binder实现接口并实现业务。 改造后的代码如下:

public class MusicPlayerService extends Service {
       
    private XXXBinder mBinder;
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;  // 此处返回服务端的binder
    }
    
    // binder实现了IPlayer业务接口
    public xxxBinder extends Binder implemention IPlayer {
        @Override
        public void onTransact(int code, Parcel data, Parcel reply, int flag) {
            // 这个方法很重要,由binder驱动调用
            // 根据约定的code值分别调用不同的业务方法,此处是startPlay和stop
            if (code == 0x100) {
                this.startPlay(file_path)
            } else if (code == 0x101) {
                this.stop()
            }
        }
        
        public void startPlay(String filePath) {
            Log.i("test", "startPlay");
        }
    
        public void stop() {
            Log.i("test", "stop");
        }
    }
}

至此,使用binder的代码结构就较为清晰了。以上demo一步步的改造,其实就是为了更加深刻的理解binder的使用,并向aidl靠拢,从而更好的理解aidl生成的代码。 从上代码可以看出,使用binder的代码接口基本是固定的,所以android framewrok提供了一个aidl的工具来简化这个过程。

4、aidl的使用

在对应模块上右键选择aidl,会弹出创建aidl的对话框,输入名字as会自动生成对应的aidl文件。 在aidl文件中加入自己的业务接口即可。 build一下as就会根据aidl自动生成相应的接口类和binder类,具体文件此处省略。

在这里插入图片描述

需要注意的是,aidl中只支持Parcelable对象和原子类,如果有自定义的类需要跨进程访问时需要实现Parcelable接口。

客户端通过如下代码获取接口对象并实现调用。

class MusicClient {
    private var mIMusicPlayer: IMusicPlayer? = null

    private var mServiceConnection = object: ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            // 这个方法中提供了是本地通信还是跨进程通信的判断
            // 这里就客户端进程就获取了远程服务的binder引用了。 aidl工具生成了Stub及Proxy类封装了直接通过binder.transact调用的逻辑,让开发者只需和接口交互即可。 不必关心binder交互的细节。 
            mIMusicPlayer = IMusicPlayer.Stub.asInterface(service)
        }

        override fun onServiceDisconnected(name: ComponentName?) {

        }
    }

    fun bindMusicService(context: Context) {
        val intent = Intent(context, PlayerService::class.java)
        context.bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE)
    }
}

注意在使用aidl时,如果客户端和服务端在不同的工程时,两端的aidl文件要是一样的。实际中可以直接拷贝。

5、binder架构

此处摘抄一张binder架构图

binder架构
从这张图就可以清晰看到客户端、驱动及服务端的职责,也能更好的理解binder的交互过程。这么看下来,感觉binder驱动其实就是负责对消息进行了转发,同时对交互的过程进行了一定控制。

6、总结

1、一个类要想序列化就要实现Serializable或Parcelable接口,同理一个类要想提供跨进程服务,就必须继承binder类。 binder就像一个标记类一样,只要继承了,就有资格在进程间通信了。

2、一个binder跨进程的通信包含了客户端、服务端和binder驱动三方。

3、在应用开发中主要通过aidl工具、Service及Context.bindService实现跨进程访问。

4、aidl是一个命令行工具,协助生成跨进程调用的样板代码。

7、参考

1、《Android传奇》

2、《Android内核剖析》

3、《Android进阶解密》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

元宇宙应用领域-医疗

元宇宙(Metaverse)是一个虚拟空间,用户可以通过数字技术和设备在其中生活和工作。元宇宙由一系列相关的技术和应用组成,包括区块链、虚拟现实、增强现实、人工智能、网络安全、大数据和云计算等。 元宇宙是一种新型的虚拟空间&am…

【加解密】bcryptjs | CryptoJS | JSEncrypt | node-rsa 加密| 解密 | RSA | ASE | MD5

加解密 1、 bcryptjs 解密 - 只可加密,比对密码,不可解密 下载 npm i bcryptjs 作用:字符串加密,已加密的字符串不可破解,只可比对。优点:加密的字符不可解密缺点:已加密的字符不可解密&#…

【TI毫米波雷达笔记】IWR6843AOPEVM开箱功能测试

【TI毫米波雷达笔记】IWR6843AOPEVM开箱功能测试 我用的是IWR6843AOPEVM-G 相关资源可以在ti官网下载 要用的软件是 TI官方上位机 mmWave_Demo_Visualizer 可以用网页版 也可以用软件包 建议先上网页版看看版本支不支持对应的板子 网页版: dev.ti.com/gallery/…

CMake之安装

目录 公共选项安装目标安装文件安装目录安装导出 公共选项 install有多个签名,这些签名公用的选项有以下: DESTINATION:指定文件要安装的目录,可以是相对路径或绝对路径。 相对路径:会使用 CMAKE_INSTALL_PREFIX 作为…

rttread-nano 使用记录:rt_kprintf函数格式化打印无法左对齐

rttread-nano 使用记录:rt_kprintf函数格式化打印无法左对齐 今天用rt_kprintf函数打印输出一个表格,为了表格好看每一列我都使用格式化参数-负号符号设置为了左对齐,但是发现无法打印,也无法打印浮点数,换成微库的pri…

通过注册表显示和隐藏“我的电脑”、“回收站”等图标

注册表路径: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel 其中{208D2C60-3AEA-1069-A2D7-08002B30309D}是我的电脑,值的类型为REG_DWORD,改为0后隐藏,1代表显示。 其…

Try ‘apt --fix-broken install’ with no packages解决办法

在Ubuntu中用apt-get安装软件,系统报出Unmet dependencies错误。 Unmet dependencies. Try ‘apt --fix-broken install’ with no packages (or specify a solution) 解决方案如下: sudo apt --fix-broken install sudo apt-get update sudo apt-get u…

建筑专业应届生零基础想学习数据分析,职业发展前景如何?能学会吗?

建筑应届生零基础想学习数据分析,职业发展前景如何?能学会吗? 当然是能学会的,但如果想要有发展前景,不仅是会数据分析而已,更需要实战能力,能够结合不同的业务进行分析,掌握各种常见…

openFast中的陆上风电机组5MW_Land_DLL_WTurb参数详解

文章目录 一、openFAST是什么?二、参数截图三、参数详解 一、openFAST是什么? openFAST是一种开放源代码的工具,为风能工程师提供了用于模拟和设计风力涡轮机的框架。它可以计算风力涡轮机在各种环境条件下的性能,并提供对风力涡…

HTML、CSS、 JavaScript介绍(二)

CSS 指层叠样式表 (Cascading Style Sheets),CSS定义如何显示 HTML 元素。HTML 标签原本被设计为用于定义文档内容,样式表定义如何显示 HTML 元素,就像 HTML 中的字体标签和颜色属性所起的作用那样。样式通常保存在外部的 .css 文件中。我们只…

「GPT实战」GPT接入直播间实现虚拟人互动

摘要 ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用。结合两者的优势和特点,可以探索出更多的应用场景和商业模式。例如,在元宇宙中使用ChatGPT进行自然语言交互,可以为用户提供更加智能化、个性化的服务和支持;在Ch…

清除浮动的方法

目录 清除浮动 2.1 清除浮动的方法 — ① 直接设置父元素高度 2.2 清除浮动的方法 — ② 额外标签法 2.3 清除浮动的方法 — ③ 单伪元素清除法 2.4 清除浮动的方法 — ④ 双伪元素清除法 2.5 清除浮动的方法 — ⑤ 给父元素设置overflow : hidden 清除浮动 ➢ 含义&#…

springboot+java大学生新生入学报到报道系统+jsp004

新生报到系统分为学院管理员,宿舍管理员,财务管理员,辅导员,学生五种登录身份 学院管理员界面登入后台后有个人信息的展示,可对余下的四种身份信息进行增删改查,可进行对高考信息的导入导出,对报…

藏经阁(八)LCD控制器 ILI9341 解析

文章目录 TFT屏幕介绍LCD控制器ILI9341控制器内部框图ILI9341时序详解RGB565数据格式 TFT屏幕介绍 LCD(Liquid CrystalDisplay)的全称是液晶显示器,是现在用的比较多的手机屏幕材料,特点是价格便宜,使用普及广泛,在显示的时候需要…

Spring Boot 中如何使用 Spring Cloud Alibaba 实现微服务治理

Spring Boot 中如何使用 Spring Cloud Alibaba 实现微服务治理 在现代化的微服务架构中,服务的数量和复杂度越来越高,如何有效地管理这些服务变得越来越重要。Spring Cloud Alibaba 提供了一套完整的微服务治理解决方案,包括服务注册与发现、…

自动化托盘四向穿梭车密集库|HEGERLS托盘四向穿梭车系统具有哪些核心技术?

随着国内外物流行业需求的快速上升,托盘四向穿梭式立体库因其在流通仓储体系中所具有的高效密集存储功能优势、运作成本优势与系统化智能化管理优势,已发展为仓储物流的主流形式之一。河北沃克HEGERLS根据客户需求精准发力,推出了工业级的高品…

煤矿电子封条系统 yolov7

煤矿电子封条系统通过yolov7网络模型算法,煤矿电子封条系统可以实现对煤矿井下人员的出入管理,提高对煤矿井下人员的监管效果。YOLOv7 的策略是使用组卷积来扩展计算块的通道和基数。研究者将对计算层的所有计算块应用相同的组参数和通道乘数。然后&…

chatgpt赋能python:PythonSearch:剖析目前最常用的搜索引擎

Python Search: 剖析目前最常用的搜索引擎 搜索引擎在我们日常生活中扮演着非常重要的角色,尤其是Google、Bing、Yahoo这样的大型搜索引擎。Python作为一种高效的编程语言,在搜索引擎的开发中也发挥着至关重要的作用。本文将解析目前最常用的搜索引擎&a…

chatgpt赋能python:Python说:为什么这门编程语言如此重要?

Python说:为什么这门编程语言如此重要? 在过去的十年里,Python已经成为了最受欢迎的编程语言之一。它具有明显的优势:易于学习,简单易用,支持多种应用场景和操作系统。Python说:不论是初学者还…

IDEA常用插件分享

分享几个常用的IDEA开发插件,极大的提高工作效率 1、Alibaba Java Coding Guidelines 阿里巴巴代码规约: Alibaba Java Coding Guidelines 专注于Java代码规范,目的是让开发者更加方便、快速规范代码格式。该插件在扫描代码后,将…