Android AIDL 跨进程通信

基于 Binder 的进程间通信机制详解

Stub/Proxy 双向通信 线程模型 生命周期

目录导航

一、AIDL 概述

1 AIDL 定义与核心特点

AIDL(Android Interface Definition Language)是 Android 用于跨进程通信(IPC)的接口定义语言。

  • 底层实现:基于 Binder驱动
  • 核心能力:支持方法调用、传递自定义 Parcelable对象以及双向通信(回调)
典型场景:一个进程作为服务端(Service),其他进程作为客户端,客户端可调用服务端方法,服务端也可主动回调客户端。

二、服务绑定流程

2.1 客户端绑定服务
val intent = Intent(this, BookManagerService::class.java)
bindService(intent, connection, BIND_AUTO_CREATE)
  • Intent:显式指定目标 Service 类
  • bindService:异步操作,不会阻塞 UI
  • BIND_AUTO_CREATE:若服务未启动,自动创建服务进程并调用 onCreate()onBind()
2.2 ServiceConnection 回调
private val connection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        bookManager = IBookManager.Stub.asInterface(service)
    }
    override fun onServiceDisconnected(name: ComponentName?) {
        bookManager = null // 服务意外死亡时回调
    }
}
  • onServiceConnected:绑定成功后系统回调,参数 service是服务端 onBind返回的 IBinder
  • onServiceDisconnected:仅在服务进程崩溃或被杀死时调用,主动 unbindService不会触发
2.3 服务端实现
class BookManagerService : Service() {
    private val binder = object : IBookManager.Stub() {
        override fun addBook(book: Book?) { ... }
        override fun getBookList(): List { ... }
    }
    override fun onBind(intent: Intent?): IBinder = binder
}
  • 继承 AIDL 生成的 IBookManager.Stub,实现业务方法
  • onBind返回该 Stub 实例

三、AIDL 生成代码核心:Stub 与 Proxy

1 AIDL 生成的代码结构

当定义 .aidl文件后,编译会生成同名的 Java 接口,内部包含:

  • 接口:继承 IInterface
  • Stub 抽象类:继承 Binder并实现接口
  • Proxy 类:实现接口,持有 IBinder引用,方法调用通过 transact序列化发送
2 asInterface 的作用
public static IBookManager asInterface(IBinder obj) {
    if (obj == null) return null;
    IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (iin != null && iin instanceof IBookManager) {
        return (IBookManager) iin; // 同进程 -> 直接返回 Stub
    }
    return new Proxy(obj); // 跨进程 -> 返回 Proxy
}
  • 同进程:queryLocalInterface返回非空,直接返回 Stub 实例,方法调用是普通的 Java 调用,无序列化开销
  • 跨进程:返回 Proxy 代理,每次方法调用都会打包参数 → transact→ 驱动 → 服务端 onTransact执行
Stub / Proxy 调用路径
同进程
Client → Stub(直接调用) → 无Binder开销
跨进程
Client → Proxy → transact → Binder驱动 → onTransact → Stub → 业务逻辑

四、双向通信(回调机制)

4.1 定义回调 AIDL 接口
// IBookUpdateCallback.aidl
package com.example.woyaojindachang;
import com.example.woyaojindachang.modle.Book;

interface IBookUpdateCallback {
    void onBookAdded(in Book newBook);
}
4.2 主 AIDL 接口加入注册方法
// IBookManager.aidl
import ...IBookUpdateCallback;

interface IBookManager {
    void addBook(in Book book);
    List getBookList();
    void registerCallback(IBookUpdateCallback callback);
    void unregisterCallback(IBookUpdateCallback callback);
}
4.3 客户端实现回调 Stub 并注册
private val callback = object : IBookUpdateCallback.Stub() {
    override fun onBookAdded(newBook: Book?) {
        runOnUiThread { /* 更新 UI */ }
    }
}

// 在 onServiceConnected 中
bookManager?.registerCallback(callback)
  • object : IBookUpdateCallback.Stub()创建的是一个 Binder对象(本地 Stub)
  • 将该对象传给服务端,Binder 驱动会传递其引用,服务端获得的是一个 Proxy代理
4.4 服务端管理回调并触发
private val callbacks = RemoteCallbackList()

override fun registerCallback(callback: IBookUpdateCallback?) {
    callback?.let { callbacks.register(it) }
}

private fun notifyBookAdded(book: Book?) {
    val n = callbacks.beginBroadcast()
    for (i in 0 until n) {
        try {
            callbacks.getBroadcastItem(i).onBookAdded(book)
        } catch (e: RemoteException) { }
    }
    callbacks.finishBroadcast()
}
  • RemoteCallbackList是线程安全的集合,能自动清理死进程的回调
  • beginBroadcast()finishBroadcast()成对使用,期间不可修改集合
4.5 双向通信的本质
  • 正向:客户端持有服务端的 Proxy (bookManager),调用服务端方法
  • 反向:服务端持有客户端的 Proxy(回调对象),调用客户端方法

两端互相持有对方的 Binder 引用,即可实现双向调用。角色不互换:服务端仍是数据持有方和业务核心。

五、线程模型与 ANR 注意事项

1 线程模型概述

AIDL 方法调用(包括回调)是 同步阻塞的,默认运行在:

  • 服务端方法:Binder 线程池
  • 客户端方法:调用者线程
2 客户端调用服务端

如果在 UI 线程调用,服务端方法若耗时过长(如 5 秒),会导致 ANR。

解决方案:

  • 在客户端使用后台线程(lifecycleScope.launch(Dispatchers.IO)
  • 或将 AIDL 方法声明为 oneway void addBook(in Book book);(单向调用,无返回值,不阻塞)
3 服务端回调客户端

回调方法运行在客户端的 Binder 线程池,不能直接操作 UI,必须:

  • runOnUiThread
  • 切换到主线程 Handler

六、生命周期管理

1 必须解绑

onDestroy中调用 unbindService(connection),否则会造成 Activity 泄漏。

2 主动解注册回调

onDestroyonServiceDisconnected中调用 bookManager?.unregisterCallback(callback)

注意:RemoteCallbackList会在客户端进程死亡时自动清理回调,无需额外处理。

七、自定义 Parcelable 对象传递

1 实现要求

若 AIDL 方法参数或返回值使用自定义类(如 Book),需满足:

  1. 该类实现 Parcelable接口(Kotlin 可用 @Parcelize配合插件)
  2. aidl目录下创建同包名的 .aidl文件:
    // Book.aidl
    package com.example.woyaojindachang.modle;
    parcelable Book;
  3. 在主 AIDL 文件中导入该类:import com.example.woyaojindachang.modle.Book;

八、关键源码路径(Android 14)

1 核心类与文件路径
类/文件 路径 说明
ContextWrapper frameworks/base/core/java/android/content/ContextWrapper.java Context 包装类
ContextImpl frameworks/base/core/java/android/app/ContextImpl.java Context 真正实现
bindServiceCommon() ContextImpl内部方法 bindService 真正实现
LoadedApk.getServiceDispatcher LoadedApk内部方法 包装 ServiceConnection
ActivityManagerService.bindService ActivityManagerService内部方法 系统服务处理绑定请求

九、常见问题速查

1 onServiceConnected 未调用

可能原因:清单文件未声明 Service;Intent 不匹配;权限问题

2 asInterface 返回 null

可能原因:传入的 IBinder为 null(服务端 onBind返回 null)

3 回调未触发

可能原因:服务端未调用 notifyBookAdded;未注册回调;服务端未实现 registerCallback

4 跨进程调用抛 TransactionTooLargeException

可能原因:传输数据超过 Binder 缓冲区上限(1MB),应改用文件或共享内存

5 客户端收到回调后 UI 不更新

可能原因:回调在 Binder 线程池,未切换到 UI 线程

十、总结口诀

AIDL 核心要点