使用 AIDL 进行 IPC 的流程解析和其他要点

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

由于笔者未卜先知,知道自己将写一篇关于 AIDL 的技术分析文章

于是两年前笔者写了这个项目 IPC_Sample

(手动滑稽 ~~)

两年后的现在,我在这里把这篇文章补上

AIDL 实现步骤

这里不会介绍具体的编写步骤,如果需要可以参考 Android 接口定义语言 (AIDL)

具体编写好的代码请参考上述项目 IPC_Sample

总体可以分为三步:

  1. 编写.aidl 文件
  2. 实现服务端 RemoteService
  3. 客户端调用服务端的方法

编写.aidl 文件时有以下几点需要注意:

  1. 支持的数据类型有限
  2. 所有非原语参数都需要指示数据走向的方向标记
    可以是 in、out 或 inout
    原语默认为 in,不能是其他方向
    应该将方向限定为真正需要的方向,因为编组参数的开销极大
  3. 只支持方法

AIDL 文件并不是必需的,只是有了这个文件,系统会帮助我们自动生成 IRemoteService.java 这个类

AIDL 的包结构在服务端和客户端要保持一致,否则反序列化将会失败!

onTransact() 方法在 Binder 线程池中执行,如果要进行 UI 操作,可以利用 Handler 切换到主线程执行

客户端挂起直到服务端返回,所以要注意避免 ANR

IRemoteService

下面的分析还是以这个项目为例进行讲解 IPC_Sample

当我们写完 .aidl 文件后,AS 会自动在 build 子目录下为我们生成 IRemoteService ,这个接口的具体结构如下:

public interface IRemoteService extends android.os.IInterface{
	public static abstract class Stub extends android.os.Binder implements com.sbingo.ipc_sample.AIDL.IRemoteService {
		……
		private static class Proxy implements com.sbingo.ipc_sample.AIDL.IRemoteService {
			……
		}
	}
    public int getPid() throws android.os.RemoteException;

    public int add(int a, int b) throws android.os.RemoteException;

	public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

理解了这个类的结构, AIDL 的原理就基本理解了

Stub

static abstract

抽象内部类,继承了 Binder,实现了 IRemoteService

这是服务端,如果客户端和服务端在同一进程,Stub 对象将被返回给客户端

Proxy

static

内部类的代理类,实现了 IRemoteService

如果客户端和服务端不在同一进程,代理对象将被返回给客户端

流程解析

现在我们来分析整个流程,搞懂背后的原理

客户端持有服务端引用

客户端在成功连接服务端后的代码如下:

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    mService = IRemoteService.Stub.asInterface(service);	//1
    mCallbackText.setText("已连接服务端");
    mIsBound = true;
}

注释1处调用了 IRemoteService.StubasInterface() 方法,跟进去继续看:

/**
 * Cast an IBinder object into an com.sbingo.ipc_sample.AIDL.IRemoteService interface,
 * generating a proxy if needed.
 */
public static com.sbingo.ipc_sample.AIDL.IRemoteService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.sbingo.ipc_sample.AIDL.IRemoteService))) {
        return ((com.sbingo.ipc_sample.AIDL.IRemoteService) iin);
    }
    return new com.sbingo.ipc_sample.AIDL.IRemoteService.Stub.Proxy(obj);
}

这个方法的作用就是判断客户端和服务端是否为同一进程

是的话返回类 Stub 的实例

否则返回代理类 Proxy 的实例

客户端调用服务端方法

这里以 add() 方法为例讲解调用过程,并且假设客户端和服务端在不同的进程中

private void tryToAdd() {
    if (!mIsBound) {
        Toast.makeText(MainActivity.this, "请先绑定服务端", Toast.LENGTH_SHORT).show();
        return;
    }
    if (TextUtils.isEmpty(num1.getText().toString()) ||
            TextUtils.isEmpty(num2.getText().toString())) {
        Toast.makeText(MainActivity.this, "请输入以上两个数字", Toast.LENGTH_SHORT).show();
    } else {
        try {
            int r = mService.add(Integer.valueOf(num1.getText().toString()), Integer.valueOf(num2.getText().toString()));	
            mCallbackText.setText("相加结果:" + r);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

第11行是调用的代码,mService 就是之前保存的代理类 Proxy 的实例

于是我们查看代理类 Proxyadd() 方法

@Override
public int add(int a, int b) 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(a);
        _data.writeInt(b);
        //这里会阻塞等待 mRemote 的 onTransact() 返回的计算结果
        mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
        //计算结果保存在 _reply 中
        _reply.readException();
        _result = _reply.readInt();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    //返回结果给客户端
    return _result;
}

配合注释,这段代码很容易理解

第12行的 mRemote 就是之前的 asInterface() 方法传入的 IBinder 对象,在本项目中就是RemoteService.java 中传入的 Stub 实例

于是我们查看 StubonTransact() 方法

@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 TRANSACTION_getPid: {
            data.enforceInterface(descriptor);
            int _result = this.getPid();
            reply.writeNoException();
            reply.writeInt(_result);
            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;
        }
       	……
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

第20行的 this 指的是 RemoteService.java 中传入的 Stub 实例

public class RemoteService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int getPid() {
            return Process.myPid();
        }

        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
                               float aFloat, double aDouble, String aString) {
            // Does nothing
        }
    };
}

第17行就是真正执行计算的地方了,自此计算结果一路返回至客户端,调用成功!

系统服务 Binder 示例

系统服务中使用了大量的 Binder

这里以常见的 startActivity() 为例,它涉及到的 Binder相关类如下

interface IActivityManager extends IInterface	//接口
ServiceManager.getService("activity")	//真正的服务提供者
abstract class ActivityManagerNative extends Binder implements IActivityManager	// Binder 子类
class ActivityManagerProxy implements IActivityManager	//代理类

可以看到,这些和之前分析的原理是一样的

有兴趣的可以自行查看源码,这里不再细述了

服务端死亡通知

如果服务端进程因为某种异常而终止了,它给客户端使用的功能就会受到影响

为解决这一问题,接口 IBinder 提供了死亡通知,可以在服务端终止时通知客户端,与此相关的两个方法如下:

/**
* 注册死亡通知
*/
linkToDeath()       

/**
* 移除死亡通知
*/
unlinkToDeath()

服务端死亡时系统会回调客户端的 binderDied() 方法,此时应该移除之前绑定的 Binder 代理,然后重新绑定远程服务

当然,还有更简单的方法可以得知服务端已经挂了,那就是 onServiceDisconnected() 回调

这个方法和上述死亡通知的区别就是:onServiceDisconnected() 方法在主线程被回调,可以操作 UI

RemoteCallbackList

对象的跨进程传输本质上是反序列化的过程,所以 AIDL 中的自定义对象都要实现 Parcelable 接口,对象传输后得到的是不同的对象

正因为如此,如果客户端用常见写法在服务端设置了某种监听器,解注册时会失败,因为客户端传入的解注册对象在服务端并不存在!

为解决这一问题,系统提供了专门用于跨进程解注册的类 RemoteCallbackList<E extends IInterface>

它主要有以下三大功能:

  1. 成员变量 mCallbacksArrayMap 类型,它的 keyIInterface 类型,轻松实现解注册功能
  2. 注册 listener 时同时注册死亡通知,所以客户端进程终止后,会自动移除客户端所注册的 listener
  3. 内部自动实现了线程同步功能

它的使用方式主要涉及以下3个方法:
beginBroadcast()
getBroadcastItem()
finishBroadcast()

权限验证

如果服务端只允许有权限的客户端调用,可以增加权限验证

权限验证失败就无法调用我们的服务

onBind()onTransact() 方法中进行 permission 验证

Android 进程间通信(IPC):理解Binder & AIDL 首先,我们需要在文件中,定义我们的远程加法服务接口。在目录下,手动创建一个名为aidl的文件夹。在aidl。在文件夹上右键 -> New -> AIDL File,命名为。// 定义这个 AIDL 接口所属的包名,必须与你的 Android 项目的 Java 包名一致。// 这是一个远程接口,它定义了客户端可以调用服务端哪些方法。// 想象成一份客户端服务端都认可的“合同”。/*** 定义一个远程方法,用于计算两个整数的 阅读详情

相关推荐

Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解

在上一篇博文中介绍了一种轻量级的跨进程通讯方案-Messenger,Messenger实现起来非常简单,其底层原理也是AIDL,更像是一个简易版的AIDL,但简单的东西往往也有其局限性,Messenger的主要作用是传递消息,它无法实现RPC功能也就是无法让我们在客户端本地就能调用远程的方法,而且Messenger是以串行的方式处理,无法同时处理多个请求,只能一个一个的处理。而AIDL就可以很好弥补Messenger的不足,虽然实现起来相对复杂一些,但它功能强大,无疑是跨进程通讯的首选方案。

m0_56146626的博客 2601

AIDL使用以及原理分析

AIDL使用以及IPC原理分析(进程间通信)概要为了大家能够更好的理解android的进程间通信原理,以下将会从以下几个方面讲解跨进程通讯信: 1. 必要了解的概念 2. 为什么要使用aidl进程间通信 3. 可能遇到的问题以及解决办法 4. aidl使用,通过android提供的aidl实现一个进程间通信 5. 不使用aidl,手动编写Binder实现进程间通信 6. 分析aidl的原理,梳理a

Android开源技术交流 1万+

AIDLIPC浅谈

1.IPCAIDL产生原因:   AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序

whminyi的专栏 892

IPCBinderAIDL与Intent之间区别与联系

IPCBinderAIDL与Intent之间区别与联系 博客分类:  Android 移动 手机       很多文章提到的的IPCBinderAIDL都是混在一起说,有时候好像觉得它们是一样的,但是又有区别,那倒底有什么共同点与不同呢,还有Intent与它们有什么关系呢?      首先看看各自的全称与中文名:      IPC:Inter-Process C

jinxinliu1的专栏 6095

IPC通信:AIDL实现

IPC(Inter-Process Communication) 进程间通信,是指两个不同进程之间数据交换的过程。AIDL:Android Interface Definition Language,即Android接口定义语言。其原理用法,网上也是各种介绍,初学本着用过则为熟悉AIDL的心理。自己写一遍,加深印象理解。首先,梳理一下流程:1、编写aidl文件;2、生成对应的java文件;3、...

你努力或许没人知道,但,不努力都看得出来! 1224

android IPC通信(下)-AIDL

android IPC通信(上)-sharedUserId&&Messenger   android IPC通信(中)-ContentProvider&&Socket   这篇我们将会着重介绍AIDL使用方式原理,要介绍AIDL先要简单介绍一下 Binder,而且 Messenger,ContentProvider AIDL 的最底层都是使用Binder。   相关博客介绍:

Shawn_Dut的专栏 2万+

Android 进阶——AIDL 详解之AIDL 借助Binder 实现IPC背后的核心思想原理(三)

AIDL 借助Binder 实现IPC背后的核心思想原理

最美不过,心中有梦,身旁有你! 2363

Framework掌握不全被面试官怼?这里有一份Android字节大牛整理的Framework内核精编解析笔记与面试解析

2401_84408437的博客 1019

Android之BinderAIDL原理

前言 插件化技术火热已久,为什么会有插件化,时势造英雄吧,随着移动互联网的快速发展,业务的飞速增长,如何在有限时间给用户提供高质量的APP,当线上出现各种BUG,如何快速修复并发布上线,插件化的意义也就在这里了。目前插件化解决方案分为两个方向,一是以张勇的DroidPlugin框架为代表的动态替换方案,对Android底层的各种类进行Hook,来达到加载插件的四大组件的目的;二是以任玉刚的DL框架为代表的静态代理方案,通过ProxyActivity统一加载插件中的Activity。如何学好插件化这不是一件容

feelinghappy的专栏 269

Android~AIDL完成跨进程通信(AIDL文件解析

一、如何使用AIDL文件来完成跨进程通信?        AIDL的作用,就是让两个不同的应用间通过Service进行通信(进程通讯IPC),并且远程的Service可以处理多线程。简单来讲就是,两个应用,一个应用对外提供一个远程Service,其他的应用可以并发地访问这个Service,即:C/S模式。 二、AIDL 的编写主要为以下三部分:        创建一个A

~~~的博客 928

IPCAIDL那些事儿

目录IPCAIDL如何使用服务端实现AIDL接口创建创建service客户端实现AIDL过程分析defaultStubDESCRIPTORasInterfaceonTransact(code,data,reply,flags)Stub.ProxymRemote定义的接口下集预告 IPC IPC是Inter-Process Communication的缩写,含义就是进程通信或者跨进程通讯.在这里我们需要理解什么是进程?什么是线程?通常情况下一个app就是一个进程,一个进程可以有多个线程,因此进程线程是包含与

GYH的博客 572

AIDL设计思路

AIDL设计思路 文章目录AIDL设计思路AIDL1.实现一个自定义的AIDL2.IDE自动生成IMyService.java文件3.本地IPC-Stub本地的使用4.远端IPC使用5.aidl整体流程 AIDL 1.实现一个自定义的AIDL // IMyService.aidl package com.yetao.testaidl; interface IMyService { /** * Demonstrates some basic types that you can use a

yetaoii的博客 322

Android中AIDL使用方法及流程介绍

创建一个接口,再里面定义方法package com.example.taidl; interface ICalcAIDL { int add(int x , int y); int min(int x , int y ); } build一下gen目录下会生成ICalcAIDL.java文件,下面是完整代码public interface ICalcAIDL e...

CallmeZhe的博客 675

BinderAIDL使用详解

Binder 原理 1、概述 Android 系统中,涉及到多进程间的通信底层都是依赖于 Binder IPC 机制。例如当进程 A 中的 Activity 要向进程 B 中的 Service 通信,这便需要依赖于 Binder IPC。不仅于此,整个 Android 系统架构中,大量采用了 Binder 机制作为 IPC(进程间通信,Interprocess Communication)方案。 ...

performance 1024

BinderAIDL

Binder AIDL常见概念1.IPC2.Binder3.AIDLBinderAIDL关系Binder机制如何实现进程间通信 常见概念 1.IPC IPC:Inter-Process Communication,进程间的通信或跨进程通信 2.Binder Binder是Android的一个类,它实现了IBinder接口。 从IPC角度来说,Binder是Android中的一种跨进程通信方式。通过这个Binder对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务基于AIDL服务

amynn的博客 649

AndroidIPC-AIDL

AndroidIPC-AIDL 一、概述 AIDL意思即Android Interface Definition Language,翻译过来就是android接口定义语言,是用于定义服务客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此生成的一个Interface的实例代码,AIDL其实是...

qq_43621019的博客 209

知识点-----AIDL(二)简单的aidl实现,自定义实体类

服务端 1、创建实体类:如BookBean,且BookBean要实现Parcelable接口,且要手动添加readFromParcel方法(不然只支持 in 的定向tag) /** * 实现序列化的实体类 * &lt;p&gt; * 序列化:由于不同的进程有着不同的内存区域,且相互之间不能直接访问, * 所以我们必须将要传输的数据转化为能够在内存之间流通的形式,这个转换的过程就是序列...

qq_36968707的博客 1879

Android AIDL实例解析

Android AIDL实例解析

qq_43661309的博客 142
上一篇: 序列化接口 Serializable 和 Parcelable
下一篇: 算法的复杂度分析到底是什么?
binaryshao
博客等级 码龄11年 52粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值