[Android AIDL] --- AIDL原理简析

news/2024/4/16 3:14:15

上一篇文章已经讲述了如何在Android studio中搭建基于aidl的cs模型框架,只是用起来了,这次对aidl及cs端如何调用的原理进行简单分析

1 创建AIDL文件

AIDL 文件可以分为两类。
一类是用来定义接口方法,声明要暴露哪些接口给客户端调用;
一类用来声明实现了 Parcelable 接口的数据类型,以供其他 AIDL 文件使用那些非默认支持的数据类型。
在 AIDL 文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下。
默认情况下,AIDL 支持下列数据类型:

八种基本数据类型:byte、charshortintlongfloatdouble、boolean
String,CharSequence
List类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象

客户端和服务端都需要创建,我们先在server端中创建,然后复制到client端即可。在 Android Studio 中右键点击新建一个 AIDL 文件,如图所示:
在这里插入图片描述
创建完成后,系统就会默认创建一个 aidl 文件夹,文件夹下的目录结构即是工程的包名,AIDL 文件就在其中。如图所示:
在这里插入图片描述
文件中会有一个默认方法,可以删除掉,也可以新增其他方法。

2 实现接口

创建或修改过 AIDL 文件后需要 build 下工程,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件(例如,IRemoteService.aidl 生成的文件名是 IRemoteService.java),在进程间通信中真正起作用的就是该文件。生成的接口包含一个名为 Stub 的子类(例如,IRemoteService.Stub),该子类是其父接口的抽象实现,并且会声明 AIDL 文件中的所有方法。
如要实现 AIDL 生成的接口,请实例化生成的 Binder 子类(例如,IRemoteService.Stub),并实现继承自 AIDL 文件的方法。
以下是使用匿名内部类实现 IRemoteService 接口的示例:

private final IRemoteService.Stub binder = new IRemoteService.Stub() {public int getPid(){return Process.myPid();}public void basicTypes(int anInt, long aLong, boolean aBoolean,float aFloat, double aDouble, String aString) {// Does nothing}
};

现在,binder 是 Stub 类的一个实例(一个 Binder),其定义了服务端的 RPC 接口。

3 server端公开接口

在为服务端实现接口后,需要向客户端公开该接口,以便客户端进行绑定。创建 Service 并实现 onBind(),从而返回生成的 Stub 的类实例。以下是服务端的示例代码:

public class RemoteService extends Service {private final String TAG = "RemoteService";@Overridepublic void onCreate() {super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {// Return the interfaceLog.d(TAG, "onBind");return binder;}private final IRemoteService.Stub binder = new IRemoteService.Stub() {public int getPid() {return Process.myPid();}public void basicTypes(int anInt, long aLong, boolean aBoolean,float aFloat, double aDouble, String aString) {Log.d(TAG, "basicTypes anInt:" + anInt + ";aLong:" + aLong + ";aBoolean:" + aBoolean + ";aFloat:" + aFloat + ";aDouble:" + aDouble + ";aString:" + aString);}};
}

我们还需要在 Manefest 文件中注册我们创建的这个 Service,否则客户端无法绑定服务。

      <serviceandroid:name=".RemoteService"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.example.aidl"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></service>

4 client 端调用IPC方法

当客户端(如 Activity)调用 bindService() 以连接此服务时,客户端的 onServiceConnected() 回调会接收服务端的 onBind() 方法所返回的 binder 实例。
客户端还必须拥有接口类的访问权限,因此如果客户端和服务端在不同应用内,则客户端应用的 src/ 目录内必须包含 .aidl 文件(该文件会生成 android.os.Binder 接口,进而为客户端提供 AIDL 方法的访问权限)的副本。所以我们需要把服务端的 aidl 文件夹整个复制到客户端的 java 文件夹同个层级下,不需要改动任何代码。
当客户端在 onServiceConnected() 回调中收到 IBinder 时,它必须调用 IRemoteService.Stub.asInterface(service),以将返回的参数转换成 IRemoteService 类型。例如:

IRemoteService iRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {// Called when the connection with the service is establishedpublic void onServiceConnected(ComponentName className, IBinder service) {// Following the example above for an AIDL interface,// this gets an instance of the IRemoteInterface, which we can use to call on the serviceiRemoteService = IRemoteService.Stub.asInterface(service);}// Called when the connection with the service disconnects unexpectedlypublic void onServiceDisconnected(ComponentName className) {Log.e(TAG, "Service has unexpectedly disconnected");iRemoteService = null;}
};

获得了 iRemoteService 对象,我们就可以调用 AIDL 中定义的方法了。如要断开连接,可以调用unbindService() 方法。以下是客户端的示例代码:

public class MainActivity extends AppCompatActivity {private final String TAG = "ClientActivity";private IRemoteService iRemoteService;private Button mBindServiceButton;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBindServiceButton = findViewById(R.id.btn_bind_service);mBindServiceButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String text = mBindServiceButton.getText().toString();if ("Bind Service".equals(text)) {Intent intent = new Intent();intent.setAction("com.example.aidl");intent.setPackage("com.example.aidl.server");bindService(intent, mConnection, Context.BIND_AUTO_CREATE);} else {unbindService(mConnection);mBindServiceButton.setText("Bind Service");}}});}ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {Log.d(TAG, "onServiceDisconnected");iRemoteService = null;}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.d(TAG, "onServiceConnected");iRemoteService = IRemoteService.Stub.asInterface(service);try {int pid = iRemoteService.getPid();int currentPid = Process.myPid();Log.d(TAG, "currentPID: " + currentPid + ", remotePID: " + pid);iRemoteService.basicTypes(12, 123, true, 123.4f, 123.45,"服务端你好,我是客户端");} catch (RemoteException e) {e.printStackTrace();}mBindServiceButton.setText("Unbind Service");}};
}

5 通过IPC传递对象

除了上面默认支持的数据类型,AIDL 还可以传递对象,但是该类必须实现 Parcelable 接口。而该类是两个应用间都需要使用到的,所以也需要在 AIDL 文件中声明该类,为了避免出现类名重复导致无法创建 AIDL 文件的错误,这里需要先创建 AIDL 文件,之后再创建类。
先在服务端新建一个 AIDL 文件,比如 Rect.aidl,示例如下:

// Rect.aidl
package com.example.aidl.server;// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

然后就可以创建 Rect 类了,并使之实现 Parcelable 接口。示例代码如下:

public class Rect implements Parcelable {private int left;private int top;private int right;private int bottom;public Rect(int left, int top, int right, int bottom) {this.left = left;this.top = top;this.right = right;this.bottom = bottom;}public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {public Rect createFromParcel(Parcel in) {return new Rect(in);}public Rect[] newArray(int size) {return new Rect[size];}};private Rect(Parcel in) {readFromParcel(in);}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeInt(left);out.writeInt(top);out.writeInt(right);out.writeInt(bottom);}public void readFromParcel(Parcel in) {left = in.readInt();top = in.readInt();right = in.readInt();bottom = in.readInt();}@Overridepublic int describeContents() {return 0;}@NonNull@Overridepublic String toString() {return "Rect[left:" + left + ",top:" + top + ",right:" + right + ",bottom:" + bottom + "]";}
}

这样我们就可以在之前创建的 IRemoteService.aidl 中新增一个方法来传递 Rect 对象了,示例代码如下:

// IRemoteService.aidl
package com.example.aidl.server;
import com.example.aidl.server.Rect;// Declare any non-default types here with import statementsinterface IRemoteService {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);int getPid();void addRectInOut(inout Rect rect);
}

注意这里需要明确导包:

import com.example.aidl.server.Rect;

然后将新增的 Rect.aidl 文件和 Rect.java 文件还有修改的 IRemoteService.aidl 文件同步到客户端相同路径下,如图所示:
在这里插入图片描述
build 下工程,就可以在客户端调用到该 addRectInOut 方法了。示例代码如下:

    ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {iRemoteService = IRemoteService.Stub.asInterface(service);try {iRemoteService.addRectInOut(new Rect(1, 2, 3, 4));} catch (RemoteException e) {e.printStackTrace();}}};

http://www.ppmy.cn/news/1072575.html

相关文章

学弟学妹们!要开学了,这些好物一定要提前备

开学的脚步近了&#xff0c;近了&#xff0c;神兽归笼&#xff0c;万物更新&#xff0c;大家迎接开学季的阵仗堪比迎接春天了。灵魂发问&#xff1a;开学装备备齐了吗&#xff1f;神兽们的情绪调整好了吗&#xff1f;自己要不要再回回炉&#xff0c;充充电&#xff1f;这次整理…

【VScode模型部署】输出结果不一致的问题

问&#xff1a;在Python中训练好的pb模型&#xff0c;拿到VScode的C&#xff0b;&#xff0b;程序中进行调用&#xff0c;结果在相同的数据下产生不同的输出结果 ChatGPT&#xff1a; 如果您在Python中训练好了一个.pb模型&#xff0c;并尝试在VSCode的C程序中进行调用&#xf…

新亮点!安防视频监控/视频集中存储/云存储平台EasyCVR平台六分屏功能展示

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

(动态规划) 剑指 Offer 60. n个骰子的点数 ——【Leetcode每日一题】

❓ 剑指 Offer 60. n个骰子的点数 难度&#xff1a;中等 把 n 个骰子扔在地上&#xff0c;所有骰子朝上一面的点数之和为 s 。输入 n&#xff0c;打印出s的所有可能的值出现的概率。 你需要用一个浮点数数组返回答案&#xff0c;其中第 i 个元素代表这 n 个骰子所能掷出的点…

Kubernetes技术--k8s核心技术Controller控制器

1.Controller概述 Controller是在集群上管理和运行容器的对象。是一个实际存在的对象。 2.pod和Controller之间的关系 pod通过controller实现应用的运维,包括伸缩、滚动升级等操作。 这里pod和controller通过label标签来建立关系。如下所示: 3.Deployment控制器应用场景 -1:…

前后端分离不存在会话,sessionid不一致问题

目录 1.使用拦截器解决跨域的示例&#xff1a; 2.使用redis&#xff0c;不使用session 前后端不分离项目我们可以通过session存储数据&#xff0c;但是前后端分离时不存在会话&#xff0c;每次请求sessionid都会改变&#xff0c;当值我们储存的数据不能取出来。 1.使用拦截器…

K8S-集群管理

目录 一、pod资源限制&#xff08;resources&#xff09; 二、重启策略&#xff08;restartPolicy&#xff09; 三、扩容缩容 1.手动扩容 2.自动扩容 2.1、数据采集组件 2.1.1、部署 2.2、HPA 2.2.1、案例 2.2.1.1、HPA基于cpu自动扩缩容 2.2.1.2、HPA基于内存自动扩…

力扣71. 简化路径

71. 简化路径 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本身&a…

MySQL数据库备份及恢复

数据备份的重要性 1、备份的主要目的是灾难恢复 2、在生产环境中&#xff0c;数据的安全性至关重要 3、任何数据的丢失都可能产生严重的后果 4、造成数据丢失的原因 备份类型(重点) 1、物理备份 数据库备份可以分为物理备份和逻辑备份。物理备份是对数据库操作系统的物…

企业网络安全:威胁情报解决方案

什么是威胁情报 威胁情报是网络安全的关键组成部分&#xff0c;可为潜在的恶意来源提供有价值的见解&#xff0c;这些知识可帮助组织主动识别和防止网络攻击&#xff0c;通过利用 STIX/TAXII 等威胁源&#xff0c;组织可以检测其网络中的潜在攻击&#xff0c;从而促进快速检测…

JavaScript 执行上下文和作用域链

执行上下文 执行上下文决定了变量和函数可以访问哪些数据。 一个执行上下文就对应一个仅后台可访问的变量对象&#xff0c;其中保存有该上下文的局部变量、参数和函数声明。 最外层的上下文称为全局上下文。宿主环境不同&#xff0c;全局上下文的关联对象就不同。在浏览器中…

List<Map>操作汇总

分组 List<Map> mapList new ArrayList<>(); Map<String,List<Map>> mapListGroup mapList.stream().collect(Collectors.groupingBy(e->e.get("xxx").toString())); 最大值最小值 int max maps.stream().mapToInt(e -> new Inte…

数学建模:熵权法

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 熵权法 构建原始矩阵 D a t a Data Data 形状为 m ∗ n m *n m∗n &#xff0c;其中 m m m 为评价对象&#xff0c; n n n 为评价指标。对 D a t a Data Data矩阵的指标进行正向化处理&#xff0c;得到…

我们的第一个 Qt 窗口程序

Qt 入门实战教程&#xff08;目录&#xff09; Windows Qt 5.12.10下载与安装 为何使用Qt Creator开发QT 本文介绍用Qt自带的集成开发工具Qt Creator创建Qt默认的窗口程序。 本文不需要你另外安装Visual Studio 2022这样的集成开发环境&#xff0c;也不需要你再在Visual St…

RISC-V Linux系统kernel制作

文章目录 1、下载2、编译 1、下载 Linux 官网地址:https://www.kernel.org $ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.181.tar.xz $ tar xvf linux-5.10.181.tar.xz $ cd linux-5.10.1812、编译 安装依赖 $ sudo apt-get install -y flex bison bui…

c刷题(三)

程序运行结果 int a, b, c; a 5; c a; b c, c, a, a; b a c; printf("a %d b %d c %d\n", a, b, c); line3&#xff1a;c6&#xff0c;a6&#xff1b; line4&#xff1a;(逗号表达式&#xff0c;从左向右计算&#xff0c;结果为最后一个表达式)c8&#xff…

Python进阶(1)

Python进阶&#xff08;1&#xff09; 文章目录 Python进阶&#xff08;1&#xff09;一&#xff1a;发送请求&#xff08;GET/POST&#xff09;1.1、方式一&#xff1a;http.client&#xff0c;以https方式为例1.1.1、GET1.1.2、POST 1.2、方式二&#xff1a;requests&#xf…

【校招VIP】产品面试之面试官的真实意图

考点介绍&#xff1a; 大厂面试时&#xff0c;面试官提出的问题除了了解经历和想法外&#xff0c;最看重的是思维逻辑能力、团队协作能力和协调能力。 『产品面试之面试官的真实意图』相关题目及解析内容可点击文章末尾链接查看&#xff01; 一、考点题目 1. 你遇到的最大的…

【UE5】给模型指定面添加自定义材质

实现步骤 1. 首先我们向UE中导入一个简单的模型&#xff0c;可以看到目前该模型的材质插槽只有一个&#xff0c;当我们修改材质时会使得模型整体的材质全部改变&#xff0c;如果我们只想改变模型的某些面的材质就需要继续做后续操作。 2. 选择建模模式 3. 在模式工具栏中点击…
最新文章