Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务

2024-06-07 20:08

本文主要是介绍Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章
Android 进程间通信(一) – Android 多进程模式
Android 进程间通信(二) – 理解 Binder 的机制
Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务

上一章,已经学习了 Binder 的通信原理,这里再通过 AIDL 了,再来捋一遍,并自己写个 Binder。

如果你对 AIDL 不熟悉,可以参考这篇文章 AIDL使用详解及进程回调

一. AIDL 基本使用

这里也是用上面的代码,首先是任务类 TaskInfo,需要继承 Parcelable 接口,让as 帮你实现方法即可,如下:

public class TaskInfo implements Parcelable {public int id;public String url;public int progress;public TaskInfo() {}protected TaskInfo(Parcel in) {id = in.readInt();url = in.readString();progress = in.readInt();}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(id);dest.writeString(url);dest.writeInt(progress);}@Overridepublic int describeContents() {return 0;}public static final Creator<TaskInfo> CREATOR = new Creator<TaskInfo>() {@Overridepublic TaskInfo createFromParcel(Parcel in) {return new TaskInfo(in);}@Overridepublic TaskInfo[] newArray(int size) {return new TaskInfo[size];}};@Overridepublic String toString() {return "TaskInfo{" +"id=" + id +", url='" + url + '\'' +", progress=" + progress +'}';}
}

然后是 TaskInfo.aidl

//注意这个 TaskInfo 必须是在 com.example.ipcdemo 下,不如会提示找不到。
parcelable TaskInfo;

IRemoteService.aidl :

//记得导入 TaskInfo.aidl 的包
import com.example.ipcdemo.TaskInfo;
interface IRemoteService {//两数之和int add(int num1,int num2);//添加一个任务TaskInfo addTask(in TaskInfo info);
}

然后 build 一下,就会发现,在 build 下生成了 IRemoteService.java
在这里插入图片描述

接着看这个 IRemoteService 的 java ,代码有点长,可以略过:

public interface IRemoteService extends android.os.IInterface
{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.example.ipcdemo.IRemoteService{private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.example.ipcdemo.IRemoteService interface,* generating a proxy if needed.*/public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {return ((com.example.ipcdemo.IRemoteService)iin);}return new com.example.ipcdemo.IRemoteService.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder(){return this;}@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{java.lang.String descriptor = DESCRIPTOR;switch (code){case INTERFACE_TRANSACTION:{reply.writeString(descriptor);return true;}case TRANSACTION_add:{data.enforceInterface(descriptor);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}case TRANSACTION_addTask:{data.enforceInterface(descriptor);com.example.ipcdemo.TaskInfo _arg0;if ((0!=data.readInt())) {_arg0 = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(data);}else {_arg0 = null;}com.example.ipcdemo.TaskInfo _result = this.addTask(_arg0);reply.writeNoException();if ((_result!=null)) {reply.writeInt(1);_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);}else {reply.writeInt(0);}return true;}default:{return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.example.ipcdemo.IRemoteService{private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote = remote;}@Override public android.os.IBinder asBinder(){return mRemote;}public java.lang.String getInterfaceDescriptor(){return DESCRIPTOR;}//两数之和@Override public int add(int num1, int num2) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(num1);_data.writeInt(num2);boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().add(num1, num2);}_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}//添加一个任务@Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();com.example.ipcdemo.TaskInfo _result;try {_data.writeInterfaceToken(DESCRIPTOR);if ((info!=null)) {_data.writeInt(1);info.writeToParcel(_data, 0);}else {_data.writeInt(0);}boolean _status = mRemote.transact(Stub.TRANSACTION_addTask, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().addTask(info);}_reply.readException();if ((0!=_reply.readInt())) {_result = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(_reply);}else {_result = null;}}finally {_reply.recycle();_data.recycle();}return _result;}public static com.example.ipcdemo.IRemoteService sDefaultImpl;}static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);public static boolean setDefaultImpl(com.example.ipcdemo.IRemoteService impl) {if (Stub.Proxy.sDefaultImpl == null && impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.example.ipcdemo.IRemoteService getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}/** Default implementation for IRemoteService. */public static class Default implements com.example.ipcdemo.IRemoteService{//两数之和@Override public int add(int num1, int num2) throws android.os.RemoteException{return 0;}//添加一个任务@Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException{return null;}@Overridepublic android.os.IBinder asBinder() {return null;}}//两数之和public int add(int num1, int num2) throws android.os.RemoteException;//添加一个任务public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException;

在解释之前,先了解一些概念:

  • IInterface : AIDL 文件必须继承的接口,它只有一个方法 IBinder asBinder() ,实现它的类,代表的是能够跨进程传输 Binder 对象,或者 Binder 代理对象,比如上面的代码中,返回this,表示 Stub 这个内部类是具有跨进程的作用的。
  • IBinder : 实现这个接口的对象具备跨进程书传输的能力,在跨进程数据流经驱动时,驱动会识别 Binder 类型的数据,从而自动完成不同进程 Binder 本地对象以及 Binder 代理对象的转换。

二. 分析Binder 流程

首先,我们看看我们在服务中时怎么构建 Binder 服务的:

    //AIDL 的服务,IRemoteService.Stub mBinder = new IRemoteService.Stub() {@Overridepublic int add(int num1, int num2) throws RemoteException {/*** 这里为具体的实现方法,比如直接返回两数之和*/Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);return (num1 + num2);}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {Log.d("zsr", "收到客户端的信息: "+info);info.id = 0;info.progress = 50;// mHandler.sendEmptyMessage(1);// mTaskInfo = info;return info;}};

可以看到,使用的是 IRemoteService 的子类 Stub ,那我们从这个类开始分析好了。

DESCRIPTOR
看到在 Stub 子类中,实现了一个标记字符串,用包名加类型表示:

 private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";

然后在它的构造方法中,注册当前的 Binder 和 字符串:

    public Stub() {this.attachInterface(this, DESCRIPTOR);}

后面需要从 ServerManager 中拿到binder 时,就需要这个字符串了,比如:

queryLocalInterface(DESCRIPTOR)

asInterface
将服务端的 Binder 对象转换成客户端所有的 AIDL 对象;比如:

IRemoteService mBinder = IRemoteService.Stub.asInterface(service);

这个转换是分进程的,如果同个进程,则直接返回自身即可,如果是跨进程,则需要使用它的代理类,去转换了。从代码也可以看出来:

    public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {return ((com.example.ipcdemo.IRemoteService)iin);}return new com.example.ipcdemo.IRemoteService.Stub.Proxy(obj);}

asBinder
从上面的介绍中已经知晓,返回的对象,具备跨进程的能力,这里返回 this,表示 Stub。

onTransact
该方法运行在服务端的Binder线程池中,数据的写入和结果读取都在这。

当客户端发起跨进程请求时,远程请求会通过底层封装后,交由次方法来处理;从 code 中,可以知道当前执行的是哪个方法,比如我们定义的,add() 和 addTask() 它们的 index 为:

    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

这样,我们就可以通过 code 去拿到对应的方法:
在这里插入图片描述
继续看 onTransact(int code, Parcel data, Parcel reply, int flags),它的参数有可以序列换和反序列化的 Parcel 参数,data 和 reply;
我们可以从 data 中取出参数(如果方法有参数的话),注意有顺序,然后把结构写入 replay ,这样当你的方法有返回值的时候,replay 返回的就是你需要的。
注意,onTransact 需要 返回 true,返回false 则表示请求失败。

IRemoteService#Proxy
从上面到说,当跨进程调用时,asInterface 返回的是代理类的实力,它也是实现 IRemoteService 接口,在Android 进程间通信(二) – 理解 Binder 的机制 我们知道,Binder 的代理模式,其实就是当 A 访问 B 的 object 方法,B 不会把的 object 给到 A,而是给它一个具体相同方法的代理类,然后A 通过这个代理把参数传递给 B,B 计算之后,把结果再传递给 A;从而实现跨进程的作用。

而它也确实是这么做的:
在这里插入图片描述
首先通过 _data 把参数都写进去,接着使用mRemote.transact 把参数传递给服务,它胡调用 onTransact 方法,然后通过线程池,把结果写入 reply ,最后再返回给 Binder,完成了跨进程的动作。

这样,我们就完成了 AIDL 的分析

三. 自定义 AIDL 服务

从上面看,AIDL 中,系统生成的逻辑比较乱,且所有类都集成在一起,但我们理清楚之后,还是比较容易理解的。那么这里,我们自己来写一个,而不是使用系统生成的。

首先,定义一个接口,让它集成 IInterface,并添加 add 和 addTask 方法,且定义好方法的 id:

public interface IRemote extends IInterface {static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.aidl.IRemote";static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addTask = (IBinder.FIRST_CALL_TRANSACTION + 1);//两数之和public int add(int num1, int num2) throws RemoteException;//添加一个任务public TaskInfo addTask(TaskInfo info) throws RemoteException;}

然后创建一个具备跨进程的实现类 IRemoteLmpl ,集成 Binder 和实现 IRemote 接口:

public class IRemoteImpl extends Binder implements IRemote {@Overridepublic int add(int num1, int num2) throws RemoteException {return 0;}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {return null;}@Overridepublic IBinder asBinder() {return this;}
}

接着,我们应该在的构造方法中,把binder 和 字符串描述注册到服务中:

    public IRemoteImpl() {this.attachInterface(this,DESCRIPTOR);}

实现供客户端调用的服务端转换的 AIDL 对象的方法 asInterface:

    public static IRemote asInterface(IBinder obj){if (obj == null) {return null;}if (obj instanceof IRemote){return (IRemote) obj;}return new Proxy(obj);}

其中 Proxy 代理类,我们后面再写。

接着,编写 IRemote 的具体实现方法 onTransact :

    @Overrideprotected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {//通过 code 对应的方法switch (code) {case TRANSACTION_add:data.enforceInterface(DESCRIPTOR);//接受到参数数据int num1 = data.readInt();int num2 = data.readInt();int result = this.add(num1, num2);reply.writeNoException();//把结果返回到返回参数中reply.writeInt(result);return true;case TRANSACTION_addTask:data.enforceInterface(DESCRIPTOR);TaskInfo info = null;if (data.readInt() != 0) {//通过反序列化拿到数据info = TaskInfo.CREATOR.createFromParcel(data);}TaskInfo resultInfo = this.addTask(info);reply.writeNoException();if ((resultInfo!=null)) {reply.writeInt(1);info.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);}else {reply.writeInt(0);}return true;default:return super.onTransact(code, data, reply, flags);}}

无法就是一些数据的转换,没啥问题,接着,实现代理类,方便跨进程调用:

 /*** 跨进程的代理类*/private static class Proxy implements IRemote {private IBinder mRemote;Proxy(IBinder iBinder) {mRemote = iBinder;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic IBinder asBinder() {return mRemote;}@Overridepublic int add(int num1, int num2) throws RemoteException {Parcel data = Parcel.obtain();Parcel replay = Parcel.obtain();int reslut;try {data.writeInterfaceToken(DESCRIPTOR);data.writeInt(num1);data.writeInt(num2);/*** 可以看到,当跨进程被调用的时候,只是把调用方的参数,给自身的方法运行,然后再把结果返回回去*/mRemote.transact(TRANSACTION_add, data, replay, 0);replay.readException();reslut = replay.readInt();} finally {data.recycle();replay.recycle();}return reslut;}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {Parcel data = Parcel.obtain();Parcel replay = Parcel.obtain();TaskInfo result;try {data.writeInterfaceToken(DESCRIPTOR);if ((info != null)) {//用数据 0,1 ,来区分是否写入成功data.writeInt(1);info.writeToParcel(data, 0);} else {data.writeInt(0);}mRemote.transact(TRANSACTION_addTask, data, replay, 0);replay.readException();if ((0 != replay.readInt())) {result = TaskInfo.CREATOR.createFromParcel(replay);} else {result = null;}} finally {data.recycle();replay.recycle();}return result;}}

这样,我们的代码就写完了
在这里插入图片描述
接着修改给其他调用的 RemoteService 的 AIDL 服务:

    //AIDL 的服务,IRemoteImpl mBinder = new IRemoteImpl() {@Overridepublic int add(int num1, int num2) throws RemoteException {/*** 这里为具体的实现方法,比如直接返回两数之和*/Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);return (num1 + num2);}@Overridepublic TaskInfo addTask(TaskInfo info) throws RemoteException {Log.d("zsr", "收到客户端的信息: "+info);info.id = 0;info.progress = 50;// mHandler.sendEmptyMessage(1);// mTaskInfo = info;return info;}};

然后,把这两个方法复制到 客户端:

在这里插入图片描述

绑定服务端服务:

//绑定 AIDL 服务
intent.setClassName("com.example.ipcdemo","com.example.ipcdemo.service.RemoteService");
    class RemoteService implements ServiceConnection{@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//  mBinder = IRemoteService.Stub.asInterface(service);mBinder = IRemoteImpl.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {}}

然后在点击事件中,调用 AIDL 方法,拿到数据:

    public void testAidl(View view) {try {if (mBinder != null) {TaskInfo info = new TaskInfo();info.url = "www.google.com";TaskInfo taskInfo = mBinder.addTask(info);int num = mBinder.add(2,3);Log.d(TAG, "zsr testAidl: 获取到服务端数据: "+taskInfo+" "+num);}} catch (RemoteException e) {e.printStackTrace();}}

打印如下;
在这里插入图片描述
这样,我们不通过 AIDL 也实现了跨进程通信了。

工程代码:https://gitee.com/zhengshaorui/IpcDemo

这篇关于Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040174

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程

Linux之进程状态&&进程优先级详解

《Linux之进程状态&&进程优先级详解》文章介绍了操作系统中进程的状态,包括运行状态、阻塞状态和挂起状态,并详细解释了Linux下进程的具体状态及其管理,此外,文章还讨论了进程的优先级、查看和修改进... 目录一、操作系统的进程状态1.1运行状态1.2阻塞状态1.3挂起二、linux下具体的状态三、进程的

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

微服务架构之使用RabbitMQ进行异步处理方式

《微服务架构之使用RabbitMQ进行异步处理方式》本文介绍了RabbitMQ的基本概念、异步调用处理逻辑、RabbitMQ的基本使用方法以及在SpringBoot项目中使用RabbitMQ解决高并发... 目录一.什么是RabbitMQ?二.异步调用处理逻辑:三.RabbitMQ的基本使用1.安装2.架构

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客