基于 Binder 的进程间通信机制详解
AIDL(Android Interface Definition Language)是 Android 用于跨进程通信(IPC)的接口定义语言。
Binder驱动Parcelable对象以及双向通信(回调)val intent = Intent(this, BookManagerService::class.java)
bindService(intent, connection, BIND_AUTO_CREATE)
Intent:显式指定目标 Service 类bindService:异步操作,不会阻塞 UIBIND_AUTO_CREATE:若服务未启动,自动创建服务进程并调用 onCreate()→ onBind()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返回的 IBinderonServiceDisconnected:仅在服务进程崩溃或被杀死时调用,主动 unbindService不会触发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
}
IBookManager.Stub,实现业务方法onBind返回该 Stub 实例当定义 .aidl文件后,编译会生成同名的 Java 接口,内部包含:
IInterfaceBinder并实现接口IBinder引用,方法调用通过 transact序列化发送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 调用,无序列化开销transact→ 驱动 → 服务端 onTransact执行// IBookUpdateCallback.aidl
package com.example.woyaojindachang;
import com.example.woyaojindachang.modle.Book;
interface IBookUpdateCallback {
void onBookAdded(in Book newBook);
}
// IBookManager.aidl
import ...IBookUpdateCallback;
interface IBookManager {
void addBook(in Book book);
List getBookList();
void registerCallback(IBookUpdateCallback callback);
void unregisterCallback(IBookUpdateCallback callback);
}
private val callback = object : IBookUpdateCallback.Stub() {
override fun onBookAdded(newBook: Book?) {
runOnUiThread { /* 更新 UI */ }
}
}
// 在 onServiceConnected 中
bookManager?.registerCallback(callback)
object : IBookUpdateCallback.Stub()创建的是一个 Binder对象(本地 Stub)Proxy代理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()成对使用,期间不可修改集合bookManager),调用服务端方法两端互相持有对方的 Binder 引用,即可实现双向调用。角色不互换:服务端仍是数据持有方和业务核心。
AIDL 方法调用(包括回调)是 同步阻塞的,默认运行在:
如果在 UI 线程调用,服务端方法若耗时过长(如 5 秒),会导致 ANR。
解决方案:
lifecycleScope.launch(Dispatchers.IO))oneway void addBook(in Book book);(单向调用,无返回值,不阻塞)回调方法运行在客户端的 Binder 线程池,不能直接操作 UI,必须:
runOnUiThread在 onDestroy中调用 unbindService(connection),否则会造成 Activity 泄漏。
在 onDestroy或 onServiceDisconnected中调用 bookManager?.unregisterCallback(callback)。
RemoteCallbackList会在客户端进程死亡时自动清理回调,无需额外处理。
若 AIDL 方法参数或返回值使用自定义类(如 Book),需满足:
Parcelable接口(Kotlin 可用 @Parcelize配合插件)aidl目录下创建同包名的 .aidl文件:
// Book.aidl
package com.example.woyaojindachang.modle;
parcelable Book;
import com.example.woyaojindachang.modle.Book;| 类/文件 | 路径 | 说明 |
|---|---|---|
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内部方法 |
系统服务处理绑定请求 |
可能原因:清单文件未声明 Service;Intent 不匹配;权限问题
可能原因:传入的 IBinder为 null(服务端 onBind返回 null)
可能原因:服务端未调用 notifyBookAdded;未注册回调;服务端未实现 registerCallback
可能原因:传输数据超过 Binder 缓冲区上限(1MB),应改用文件或共享内存
可能原因:回调在 Binder 线程池,未切换到 UI 线程