Android aidl及binder基础知识巩固

作者:义华

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驱动其实就是负责对消息进行了转发,同时对交互的过程进行了一定控制。

6、总结

1.一个类要想序列化就要实现Serializable或Parcelable接口,同理一个类要想提供跨进程服务,就必须继承binder类。 binder就像一个标记类一样,只要继承了,就有资格在进程间通信了。
2.一个binder跨进程的通信包含了客户端、服务端和binder驱动三方。
3.在应用开发中主要通过aidl工具、Service及Context.bindService实现跨进程访问。
4.aidl是一个命令行工具,协助生成跨进程调用的样板代码。

想了解Framework底层知识点的小伙伴可以参考一下,在学习过程中我也查阅和收集了一堆的参考学习文档,比如有Handler、Binder、AMS、WMS、PMS、事件分发机制、UI绘制……等等,为了便于自己查阅,将其知识点整合在一起并命名为了《Android Framework 核心学习手册》:https://qr18.cn/AQpN4J

《Framework 核心知识点汇总手册》:https://qr18.cn/AQpN4J

Handler 机制实现原理部分:
1.宏观理论分析与Message源码分析
2.MessageQueue的源码分析
3.Looper的源码分析
4.handler的源码分析
5.总结

Binder 原理:
1.学习Binder前必须要了解的知识点
2.ServiceManager中的Binder机制
3.系统服务的注册过程
4.ServiceManager的启动过程
5.系统服务的获取过程
6.Java Binder的初始化
7.Java Binder中系统服务的注册过程

Zygote :

  1. Android系统的启动过程及Zygote的启动过程
  2. 应用进程的启动过程

AMS源码分析 :

  1. Activity生命周期管理
  2. onActivityResult执行过程
  3. AMS中Activity栈管理详解

深入PMS源码:

1.PMS的启动过程和执行流程
2.APK的安装和卸载源码分析
3.PMS中intent-filter的匹配架构

WMS:
1.WMS的诞生
2.WMS的重要成员和Window的添加过程
3.Window的删除过程

《Android Framework学习手册》:https://qr18.cn/AQpN4J

  1. 开机Init 进程
  2. 开机启动 Zygote 进程
  3. 开机启动 SystemServer 进程
  4. Binder 驱动
  5. AMS 的启动过程
  6. PMS 的启动过程
  7. Launcher 的启动过程
  8. Android 四大组件
  9. Android 系统服务 - Input 事件的分发过程
  10. Android 底层渲染 - 屏幕刷新机制源码分析
  11. Android 源码分析实战

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

chatGPT提问,BGP内容

ChatGPT提问:提问框架 背景角色任务要求 动态路由:内部网关协议:如RIP ISIS OSPF 在同一个公司内部运行的路由协议 外部网关协议:如 BGP 在不同公司之间运行的路由协议 AS:自治系统 每个自治系统都有唯一的…

玩机搞机-----安卓全机型 ADB FAST 各种指令解析说明与操作【二】基础联机

安卓全机型 玩机 搞机 ADB FAST 各种指令解析说明与操作_adb线刷命令_安卓机器的博客-CSDN博客 今天对上个帖子不足的地方进行补正。方便友友进行基础的联机操作,很多时候我们用adb指令的时候会有各种奇奇怪怪的问题。例如同一个机型,同一个指令。有时候…

OpenCL编程指南-4.4矢量操作符

矢量操作符 如下描述了可用于矢量数据类型或矢量和标量数据类型组合的各类操作符。 算术操作符 算术操作符(加()、减(–)、乘(*)和除(/)),可以作用于内置整数、浮点标量和矢量数…

AWS 中的另外一种远程工具 AWS Session Manager

作者:SRE运维博客 博客地址:https://www.cnsre.cn/ 文章地址:https://www.cnsre.cn/posts/230129126154/ 相关话题:https://www.cnsre.cn/tags/aws/ 背景需求 因为项目的安全性。为了避免项目的服务器暴露在公网中。很多时候我们…

Python时间模块:time和datetime的区别与用法

前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 目录标题 前言一. Python中表示时间的两种方式:二. time三. datetime1. datetime.datetime2.datetime.timedelta 尾语 💝 一. Python中表示时间的两种方式: 时间戳:相对于197…

OpenCL编程指南-3.2OpenCL上下文

OpenCL上下文 上下文是所有OpenCL应用的核心。上下文为关联的设备、内存对象(例如,缓冲区和图像)以及命令队列(在上下文和各设备之间提供一个接口)提供了一个容器。正是上下文驱动着应用程序与特定设备以及特定设备之…

Echarts 热力图的详细配置过程

文章目录 一&#xff0c;配置过程二&#xff0c;具体实例 一&#xff0c;配置过程 引入Echarts库和热力图插件 <script src"https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script> <script src"https://cdn.jsdelivr.net/npm/…

实时聊天如何做,让客户眼前一亮(二)

让我们继续讨论一下如何利用SaleSmartly&#xff08;ss客服&#xff09;在网站中的实时聊天视图如何提供出色的实时聊天体验。 四、在实时聊天会话期间 让我们来看看我们可以确保尽可能的提高客户体验的各种方法&#xff0c;使用SaleSmartly&#xff08;ss客服&#xff09;时聊…

算法设计 || 第5题:钓鱼问题-北京大学网站在线算法题(贪心算法)

目录 &#xff08;一&#xff09;题目网址视频网址 &#xff08;二&#xff09;手写草稿思考 Part1: 慕课PPT Part2: 笨蛋的学习 &#xff08;一&#xff09;题目网址视频网址 北京大学网站在线算法题&#xff1a;1042 -- Gone Fishing (poj.org) 视频讲解&#xff08;北…

MYSQL基本操作

数据库的列类型 int&#xff1a;整型 用于定义整数类型的数据 float&#xff1a;单精度浮点4字节32位 准确表示到小数点后六位 double&#xff1a;双精度浮点8字节64位 char&#xff1a;固定长度的字符类 用于定义字符类型数据&#xff0c;固定10字节&#xff0c;如果你设定5字…

(转载)从0开始学matlab(第1天)—变量和数组

MATLAB 程序的基本数据单元是数组。一个数组是以行和列组织起来的数据集合&#xff0c;并且拥有一个数组名。数组中的单个数据是可以被访问的&#xff0c;访问的方法是数组名后带一个括号&#xff0c;括号内是这个数据所对应行标和列标。标量在 MATLAB 中也被当作数组来处理——…

实在智能与浙江工商大学官宣战略合作,共建人工智能联合实验室和实习基地

5月10日&#xff0c;实在智能与浙江工商大学正式官宣战略合作&#xff0c;并进行“人工智能联合实验室” “大学生实习实践基地”揭牌仪式。躬身入局共筑人工智能人才生态&#xff0c;这是实在智能和浙江工商大学的共同愿景&#xff0c;也是校企双方深度产学研融合、加速科技型…

【蓝桥杯国赛真题26】Scratch队列练习 少儿编程scratch图形化编程 蓝桥杯省赛真题讲解

目录 scratch队列练习 一、题目要求 编程实现 二、案例分析 1、角色分析

【Linux】进程信号(中)

在上一个文章中&#xff0c;关于信号的产生&#xff0c;还有没补充完的&#xff0c;所以在这篇文章补充一下 文章目录 1.信号的产生硬件异常产生信号a/0问题验证为8号信号 野指针问题验证为11号信号 核心转储设置核心转储大小Core与Term的区别核心转储的作用 2.信号保存1. 概念…

np读取txt、csv文件的数据

目录 1、基础参数 2、参数详解 3、应用参数示例 机器学习中使用np.loadtxt()可以高效的导入数据&#xff0c;np.loadtxt()适合.txt文件和.csv文件。但是它默认读取float类型的值。 1、基础参数 numpy.loadtxt(fname, dtype, comments#, delimiterNone, convertersNone, s…

操作系统学习笔记(二)

目录 你如何理解“临界”这个词&#xff1f; 那你如何理解在计算机领域下的“临界”这个词呢&#xff1f; 如何理解计算机领域中的“同步”这个词呢&#xff1f; 你如何理解critical这个单词&#xff1f; 单标志法&#xff1a; 双标志先检查法 双标志后检查法&#xff0…

libevent高并发网络编程 - 05_libevent实现http客户端

文章目录 1 http客户端相关的APIevhttp_uri_parse()evhttp_uri_get_scheme()evhttp_uri_get_port()evhttp_uri_get_host()evhttp_uri_get_path()evhttp_uri_get_query()evhttp_connection_base_bufferevent_new()evhttp_request_new()evhttp_make_request()evhttp_request_get_…

图像动态裁剪

1. 背景 以两级级联模型为例&#xff0c;第一级目标检测模型用于检测人员&#xff0c;第二级目标检测模型用于检测手机、对讲机等。然后实际数据采集过程中&#xff0c;手机、对讲机这些设备并不在人员的一级检测框内&#xff0c;使得二级模型训练的样本较少。 二级目标检测模…

即拼七人拼团系统开发模式,为什么这么火?

即拼七人拼团模式主要是结合了拼团模式的奖励机制和二二复制系统的排位玩法&#xff0c;将产品销售中的利润最大化让利于拼团的用户&#xff0c;刺激用户主动分享推广&#xff0c;以解决平台引流和用户活跃度的问题。 具体来说&#xff0c;即拼七人拼团模式就是用户进入平台购买…

ArrayList 和 LinkedList 之间应该怎么选择?

Joshua Bloch&#xff1a;我写了 LinkedList&#xff0c;但我自己都不用&#xff01; 对&#xff0c;Joshua Bloch 就是 LinkedList 的作者&#xff01; 如果你真信了作者的话&#xff0c;那就真的大错特错了&#xff0c;LinkedList 虽然用的没有 ArrayList 多&#xff0c;但使…