AIDL
问题
AIDL 是什么?Stub 和 Proxy 分别做什么?
答案
AIDL 是什么
AIDL(Android Interface Definition Language)是 Android 定义跨进程接口的 IDL 语言,编译器会自动生成 Binder 通信的模板代码(Stub + Proxy)。
使用流程
Step 1: 定义 AIDL 接口
IBookManager.aidl
package com.example;
import com.example.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
// oneway 异步调用
oneway void notifyUpdate();
}
Book.aidl
package com.example;
parcelable Book;
Step 2: Server 端实现 Stub
class BookService : Service() {
private val books = mutableListOf<Book>()
private val binder = object : IBookManager.Stub() {
override fun getBookList(): List<Book> = books
override fun addBook(book: Book) {
books.add(book)
}
override fun notifyUpdate() {
// 异步执行,不阻塞 Client
}
}
override fun onBind(intent: Intent): IBinder = binder
}
Step 3: Client 端绑定获取 Proxy
class BookActivity : AppCompatActivity() {
private var bookManager: IBookManager? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
// IBinder → Proxy(跨进程)或直接引用(同进程)
bookManager = IBookManager.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName) {
bookManager = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent(this, BookService::class.java)
bindService(intent, connection, BIND_AUTO_CREATE)
}
private fun loadBooks() {
// 注意:跨进程 AIDL 调用在 Binder 线程执行,应在子线程调用
lifecycleScope.launch(Dispatchers.IO) {
val books = bookManager?.bookList
}
}
}
Stub 和 Proxy 的关系
| 角色 | 位置 | 作用 |
|---|---|---|
| Stub | Server 进程 | 接收请求,解包参数,调用实际实现 |
| Proxy | Client 进程 | 打包参数为 Parcel,通过 Binder 驱动发送 |
IBookManager.Stub.asInterface(binder):
- 同进程:直接返回 Stub 本身(无需 IPC)
- 跨进程:返回 Proxy 包装类
AIDL 回调(双向通信)
ICallback.aidl
interface ICallback {
void onBookAdded(in Book book);
}
// Server 端维护回调列表
private val callbacks = RemoteCallbackList<ICallback>()
override fun registerCallback(callback: ICallback) {
callbacks.register(callback)
}
// 通知所有客户端
private fun notifyClients(book: Book) {
val count = callbacks.beginBroadcast()
for (i in 0 until count) {
callbacks.getBroadcastItem(i).onBookAdded(book)
}
callbacks.finishBroadcast()
}
RemoteCallbackList
不要用普通 List 管理远程回调。跨进程传输的 Binder 对象每次反序列化都是新对象,remove() 无法匹配。RemoteCallbackList 内部通过 Binder 的唯一标识来管理,并能自动处理进程死亡回调的解注册。
常见面试问题
Q1: in/out/inout 标记分别是什么意思?
答案:
in:数据只从 Client 传到 Server(最常用,性能最好)out:Server 修改的数据回传 Client(参数在 Server 端是空的,执行后回写)inout:双向传递,性能开销最大
默认基本类型和 String 是 in。Parcelable 对象需要显式标注。
Q2: AIDL 方法在哪个线程执行?
答案:
- Server 端:AIDL 方法在 Binder 线程池中执行(非主线程),需要注意线程同步
- Client 端:同步调用会阻塞当前线程,应避免在主线程调用耗时 AIDL 方法
- oneway:Client 调用后立即返回,Server 端仍在 Binder 线程池中串行执行