当前位置: 首页 > news >正文

IPC之AIDL那些事儿

目录

  • IPC
  • AIDL
    • 如何使用
      • 服务端实现
        • AIDL接口创建
        • 创建service
        • 客户端实现
  • AIDL过程分析
    • default
    • Stub
      • DESCRIPTOR
      • asInterface
      • onTransact(code,data,reply,flags)
    • Stub.Proxy
      • mRemote
      • 定义的接口
  • 下集预告

IPC

IPC是Inter-Process Communication的缩写,含义就是进程通信或者跨进程通讯.在这里我们需要理解什么是进程?什么是线程?通常情况下一个app就是一个进程,一个进程可以有多个线程,因此进程和线程是包含与被包含的关系.从android的角度分析就是,一个app就是一个进程,一个进程有一个主线程,就是我们的UI线程,但是如果要执行很多耗时操作的时候,就需要开启子线程去执行任务,否则就会出现ANR.这就解释了为什么会有多线程.

那么为什么会有多进程?比如一个应用因为某些原因自身需要多进程模式,至于原因可以有很多;或者是当前app需要获取其他app的数据,这就是两个进程之间的通讯,系统为我们提供了ContentProvider,所以可以证实ContentProvider是支持跨进程的;或者是一个应用为了获取更多的内存通过多进程来获取更多的内存空间,因为系统对每个app的运行内存都是做了限制的.

那么跨进程方式有很多Bundle,文件共享,ContentProvider,Messenger,AIDL,socket等都可以来实现跨进程通讯,这里我们之去深究AIDL,因为大部分的跨进程最终都是通过Binder去实现.

AIDL

为什么要叫AIDL,因为在使用该模式的时候,需要创建aidl后缀的文件.我只能想到这理由,再结合bindService去实现.

如何使用

使用AIDL要分为两个流程:服务端给予AIDL声明的接口,去实现业务逻辑;客户端通过BindService的方式讲返回的binder对象转化为AIDL接口类型去调用.

服务端实现

服务端也分为两个步骤:1.编写aidl接口,若是使用到实体类需要特别注意,下边会有体现;2.创建service内部实现aidl接口的逻辑.

AIDL接口创建

咱们先来看看创建后的目录:

首先来看看所需要的类,以及对应的创建目的.

  • BookBean.aidl 表示在AIDL声明引用BookBean对象.
  • IBookControl.aidl 表示业务逻辑的接口
  • BookBean.java 表示自定义的实体类,必须实现Parcelable

1.我们先来看看创建的实体类BookBean,必须是Parclable类型

//BookBean.java public class BookBean implements Parcelable { String name; int price; protected BookBean(Parcel in) { name = in.readString(); price = in.readInt(); } public static final Creator<BookBean> CREATOR = new Creator<BookBean>() { @Override public BookBean createFromParcel(Parcel in) { return new BookBean(in); } @Override public BookBean[] newArray(int size) { return new BookBean[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(name); parcel.writeInt(price); } }

2.创建了BookBean.java,但是AIDL在使用自定义实体类时,需要去引用声明,也就是去创建一个同名同目录的aidl文件,这里就已BookBean.aidl为例

// BookBean.aidl package com.example.csdnnote; parcelable BookBean;

我们可以看到parcelable BookBean;就表示引用具体的实体类.
3.创建业务逻辑的接口IBookControl.aidl

// IBookControl.aidl package com.example.csdnnote; import com.example.csdnnote.BookBean; interface IBookControl { List<BookBean> getBookList(); void addBook(in BookBean book); }

里面有两个接口,一个是获取所有书的getBookList,一个是增加书本的addBook接口.

有一行代码很关键:import com.example.csdnnote.BookBean;,我们需要显式的将我们想要使用的Parcelable对象引用进来.这里其实引用的是BookBean.aidl,但是BookBean.aidl内部又引用这BookBean.java.从而实现引用BookBean.java对象.

咱们来看看addBook方法,形参里多了一个in,那么in表示什么意思?
in其实是AIDL的一个对数据定向标记的语法,一共有三个in out inout.

  • in:服务端会接收到客户端传来的完整的数据,并且服务端对数据进行修改也不影响到客户端的数据
  • out:服务度接收到客户端传来的数据,但该数据不完整,该对象不为空,但是里面的字段为空,可以理解为传了一个刚刚new的对象,并且服务端对数据进行修改,会影响到客户端那头的数据.
  • inout:服务端会接收到客户端传来的完整的数据,并且服务端对数据进行修改会影响到客户端的数据.

总结

  • aidl中需要使用Parcelable对象或者是aidl对象,都需要显式import去引入.
  • 若要使用Parcelable对象,需要创建一个同名同目录的.aidl文件,内部还要去引用.
  • in out inout的使用.
创建service

既然接口都已经写好,我们可以去实现业务逻辑

public class AidlService extends Service { List<BookBean> list = new ArrayList<>(); private IBinder iBookControl = new com.example.csdnnote.IBookControl.Stub() { @Override public List<BookBean> getBookList() throws RemoteException { return list; } @Override public void addBook(BookBean book) throws RemoteException { if (!list.contains(book)) list.add(book); } }; @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return iBookControl; } }

我们可以看到,我们通过onBind方法去返回一个实现aidl接口的IBinder对象.为了证明是跨进程,我们需要在androidmanfest.xml文件里,去修改该service的进程.

<service android:name=".AidlService" android:process=":aidldemo"></service>

那么服务端的工作完成了,接下来就是看客服端.

客户端实现
public class MainActivity extends AppCompatActivity { TextView textView; IBookControl iBookControl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.tv_msg); findViewById(R.id.btn_get).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { List<BookBean> list = null; try { list = iBookControl.getBookList(); } catch (RemoteException e) { e.printStackTrace(); } textView.setText(list.toString()); } }); findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { iBookControl.addBook(new BookBean("name" + iBookControl.getBookList().size() + 1, iBookControl.getBookList().size() + 1)); } catch (RemoteException e) { e.printStackTrace(); } } }); Intent intent = new Intent(this, AidlService.class); bindService(intent, connection, BIND_AUTO_CREATE); } ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { iBookControl = IBookControl.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }

客户端其实就是通过bindService的方式,去获取绑定成功的BInder对象,在转化成AIDL接口去实现业务逻辑.

整一个AIDL过程其实很清晰.
第一步:编写AIDL接口
第二步:在Service里,返回AIDL接口内部的IBinder对象,并返回.
第三步:通过bindService的方式,去获取到返回的BInder对象,转化成AIDL接口.

这里出现了几个新词语 IBinder对象,Binder对象,看似乎AIDL的核心和他有关系,那么接下来我们来好好看看AIDL的核心过程.

AIDL过程分析

每一个aidl接口,编译后系统都是会给我们生成一个对应的java文件,如IBookControl.aidl会生成一个IBookControl.java文件,咱们先来看看代码的结构

我们可以看到IBookControl接口内部定义了两个类,一个是Default和Stub,还有两个方法.两个方法就是我们外部定义的方法.

default

咱们先来看看内部的Default类

/** Default implementation for IBookControl. */ public static class Default implements com.example.csdnnote.IBookControl { @Override public java.util.List<com.example.csdnnote.BookBean> getBookList() throws android.os.RemoteException { return null; } @Override public void addBook(com.example.csdnnote.BookBean book) throws android.os.RemoteException { } @Override public android.os.IBinder asBinder() { return null; } }

该类就表示一个方法的默认实现,若服务端没有处理,那么就由他去默认实现,Default不是重点,重点在于Stub的内部类.

Stub

现附上一个源代码,咱们抓重点一个个讲.

public static abstract class Stub extends android.os.Binder implements com.example.csdnnote.IBookControl { private static final java.lang.String DESCRIPTOR = "com.example.csdnnote.IBookControl"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.csdnnote.IBookControl interface, * generating a proxy if needed. */ public static com.example.csdnnote.IBookControl asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.csdnnote.IBookControl))) { return ((com.example.csdnnote.IBookControl)iin); } return new com.example.csdnnote.IBookControl.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_getBookList: { data.enforceInterface(descriptor); java.util.List<com.example.csdnnote.BookBean> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(descriptor); com.example.csdnnote.BookBean _arg0; if ((0!=data.readInt())) { _arg0 = com.example.csdnnote.BookBean.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); public static boolean setDefaultImpl(com.example.csdnnote.IBookControl impl) { if (Stub.Proxy.sDefaultImpl == null && impl != null) { Stub.Proxy.sDefaultImpl = impl; return true; } return false; } public static com.example.csdnnote.IBookControl getDefaultImpl() { return Stub.Proxy.sDefaultImpl; } private static class Proxy implements com.example.csdnnote.IBookControl {...} }

DESCRIPTOR

该常量表示唯一标识.

asInterface

该方法其实用于将服务端的IBinder对象转化成客户端所需的AIDL接口类型的对象.从逻辑上可以看出,先判断IBinder对象是否为空,再判断IBinder的对象是否处于一个进程中,若是则返回服务端的stub本身,若不是则返回Stub.proxy.所以可以断定Stub是运行在服务端中的,proxy是运行在客户端中的.

onTransact(code,data,reply,flags)

该方法是运行在服务端的,当客户端发起请求跨进程请求的时候,远程请求通过系统底层封装后交由此方法处理,通过code来区分所要调用的方法,data则是客户端传入的数据,通过reply可以写会返回的数据,flage则表示是否有返回值0表示双向,1表单向.

注意:该方法的返回值表示是否调用成功,true表示成功,false表示失败,所以我们在这里可以做一些权限判断来决定调用是否成功.

Stub.Proxy

proxy该内部类其实是运行在客户端中的,就拿IBookControl为例,proxy内部实现了我们定义的addBook和getBookList接口,咱们来看看proxy内部做了什么

mRemote

该IBinder表示远程IBinder,也就是服务端中的IBinder

定义的接口

其实addBook和getBookList一样,我们就讲解一个

@Override public void addBook(com.example.csdnnote.BookBean book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book!=null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().addBook(book); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }

我们可以看到一行客户端与服务端交互的代码

boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);

通过服务端的IBinder对象,去调用transact方法,那么该方法就会调用到服务端也就是Stub的onTransact,咱们可以看看传入的参数,都是一一对应的,并且最后的返回值_status,若返回false,则抛出异常,true调用成功.

经过分析,大致对AIDL的过程也就是Binder的过程有了初步的了解.不过还需要额外的说明:当客户端发起远程请求时,当前线程是会被挂起直至返回数据,所以远程操作是一个耗时操作;服务端的BInder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式.为了更好的理解流程,咱们通过一张图来总结一下.

下集预告

详细讲解Handler机制

http://www.jsqmd.com/news/1282389/

相关文章:

  • LangChain与大模型应用开发实战指南
  • 2026 青岛黄金回收渠道参考:合扬布局55家实体门店,连锁标准化交易透明可查 - 生活商业速报
  • spring cloud微服务-eureka
  • 第一次出售奢侈品包包?闵行本地交易流程与风险预警完整指南 - 讯息早知道
  • pve虚拟机迁移
  • 2018 宁夏 F. Moving On
  • 2026 荔湾黄金回收新趋势对比!全域上门 + 当场转账,这5家门店服务全面升级 - 逸程奢侈品回收中心
  • 高效时间管理:日期标记打卡法与神经科学驱动策略
  • 关于Selenium的延时等待
  • 基于LangChain的AI学习计划生成器开发实践
  • 广州训练营:军博营地效果实在 - 18002239949
  • DeepSeek说“我先去吃饭了“——AI学会摸鱼了,我们该笑还是该慌?
  • 北京黄金回收深度测评:正规店和散户的5个核心差异 - 生活时报
  • PAT甲级1059 Prime Factors 质因数分解+线性筛素数
  • 2026成都奢侈品回收避坑指南|六家持证商家测评,闲置变现首选添价收黄金奢侈品回收中心 - 二奢分享官
  • 制造业工厂 MES 选型指南,中小企业可以直接参考
  • 明末渊虚之羽2026最新免费下载附带教程
  • 三步搞定Data-Juicer:如何轻松构建AI大模型的数据处理流水线
  • 计算机毕业设计之基于SpringBoot的动漫周边网站的设计与实现
  • 计算机毕业设计之基于springboot的蛋糕在线预订系统
  • 杭州余杭区回收迪奥各类女包,全套配件加价 - 每日生活报
  • 2026年7月国内滑环十大代表性厂商全景评估:从通用品类到特种传输的全覆盖能力对比 - 科技焦点
  • 工程车辆识别挖掘机推土机洒水车压路机等识别分割数据集labelme格式3823张13类别
  • 数据投毒检测避坑:后门触发器的隐蔽性与检测盲点
  • 健身俱乐部网站开发全栈技术解析与实践
  • 2026成都钻石回收全流程实测:六家正规机构专业鉴定对比,添价收凭全套仪器与国检中检双认证获EEAT权威认可 - 二奢分享官
  • 本地AI模型部署全流程指南:从环境搭建到工程化实践
  • 比亚迪要“造人“了!8月发布人形机器人,车企集体杀入具身智能
  • 闲置钻石变现行情测评 杭州萧山区钻石回收 2026 门店红榜 - 每日生活报
  • 2026 AI 工作流避坑大全:10 个让你深夜回滚的 Prompt 工程设计错误