下面按「从 0 到可用」梳理 Android 中 AIDL + Kotlin 的用法。
AIDL 本质上是 Android Binder IPC 的一层接口定义工具,适合 跨进程通信,例如:
- App A 调用 App B 的 Service;
- 主进程调用独立进程
:remote中的 Service; - 多个进程共享某些能力;
- 需要高性能 IPC,而不是广播、ContentProvider、Socket。
一、整体结构
AIDL 一般包含三部分:
客户端 Client
|
| bindService()
|
服务端 Service
|
| onBind() 返回 Binder
|
AIDL 接口
核心流程:
- 定义
.aidl接口文件; - 编译后生成 Java Binder 接口;
- Service 实现
Stub; - 客户端通过
Stub.asInterface(IBinder)拿到远程接口; - 像调用普通方法一样调用远程方法。
二、创建 AIDL 文件
假设包名是:
com.example.aidldemo
在 Android Studio 中创建目录:
app/src/main/aidl/com/example/aidldemo/
创建文件:
IRemoteService.aidl
内容:
package com.example.aidldemo;
interface IRemoteService {
int add(int a, int b);
String getMessage();
void sendMessage(String msg);
}
注意:
.aidl文件的package必须和目录结构一致;- AIDL 默认支持一些基础类型,比如:
intlongfloatdoublebooleanStringCharSequenceListMap- 实现 Parcelable 的自定义对象。
三、Gradle 配置
通常新版本 Android Gradle Plugin 会自动识别 src/main/aidl。
如果你需要确认,可以在 build.gradle.kts 中查看:
android {
namespace = "com.example.aidldemo"
compileSdk = 35
defaultConfig {
applicationId = "com.example.aidldemo"
minSdk = 23
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
}
一般不用额外配置。
四、创建服务端 Service
创建一个 Service,例如:
package com.example.aidldemo
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
class RemoteService : Service() {
private val binder = object : IRemoteService.Stub() {
override fun add(a: Int, b: Int): Int {
Log.d("RemoteService", "add: $a + $b")
return a + b
}
override fun getMessage(): String {
Log.d("RemoteService", "getMessage")
return "Hello from RemoteService"
}
override fun sendMessage(msg: String?) {
Log.d("RemoteService", "receive message: $msg")
}
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
}
这里的:
IRemoteService.Stub()
是 AIDL 编译后自动生成的 Binder 类。
五、在 Manifest 中注册 Service
如果只是当前 App 内部跨进程,可以这样写:
<service
android:name=".RemoteService"
android:process=":remote"
android:exported="false" />
说明:
android:process=":remote"
表示这个 Service 运行在单独进程中。
如果你希望其他 App 也能绑定这个 Service,则需要:
<service
android:name=".RemoteService"
android:exported="true">
<intent-filter>
<action android:name="com.example.aidldemo.REMOTE_SERVICE" />
</intent-filter>
</service>
不过跨 App 暴露服务时建议加权限保护,后面会讲。
六、客户端绑定 Service
在 Activity 或 ViewModel 中使用:
package com.example.aidldemo
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private var remoteService: IRemoteService? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
remoteService = IRemoteService.Stub.asInterface(service)
try {
val result = remoteService?.add(3, 5)
val msg = remoteService?.getMessage()
Log.d("MainActivity", "result = $result")
Log.d("MainActivity", "msg = $msg")
remoteService?.sendMessage("Hello Service")
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
remoteService = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindRemoteService()
}
private fun bindRemoteService() {
val intent = Intent(this, RemoteService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection)
}
}
如果 Service 和 Activity 在同一个 App 中,这样就可以了。
七、跨 App 调用 AIDL Service
如果 App B 提供 Service,App A 调用它,那么两边都需要有同样的 AIDL 文件。
1. 服务端 App B
Manifest:
<service
android:name=".RemoteService"
android:exported="true">
<intent-filter>
<action android:name="com.example.aidldemo.REMOTE_SERVICE" />
</intent-filter>
</service>
Service 代码同上。
2. 客户端 App A
客户端也需要放一份完全一样的 AIDL 文件:
app/src/main/aidl/com/example/aidldemo/IRemoteService.aidl
注意:
- 包名必须一致;
- 接口名必须一致;
- 方法签名必须一致。
客户端绑定时:
private fun bindRemoteService() {
val intent = Intent("com.example.aidldemo.REMOTE_SERVICE")
intent.setPackage("com.example.aidldemo")
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
其中:
intent.setPackage("com.example.aidldemo")
是服务端 App 的包名。
Android 5.0 之后,隐式 Intent 绑定 Service 有限制,所以跨 App 绑定时建议设置 package 或 component。
八、传递自定义对象
AIDL 中经常要传对象,比如用户信息、订单信息等。
1. 创建 Kotlin Parcelable 类
推荐使用 @Parcelize。
先在 build.gradle.kts 开启插件:
plugins {
id("kotlin-parcelize")
}
定义实体类:
package com.example.aidldemo
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class User(
val id: Int,
val name: String
) : Parcelable
2. 创建 User.aidl
在:
app/src/main/aidl/com/example/aidldemo/User.aidl
写:
package com.example.aidldemo;
parcelable User;
3. 在接口中使用 User
修改 IRemoteService.aidl:
package com.example.aidldemo;
import com.example.aidldemo.User;
interface IRemoteService {
User getUser();
void updateUser(in User user);
}
注意这里:
void updateUser(in User user);
AIDL 中自定义 Parcelable 参数需要加方向修饰符:
in:客户端传给服务端;out:服务端写回客户端;inout:双向传递。
常用的是 in。
4. Service 实现
class RemoteService : Service() {
private val binder = object : IRemoteService.Stub() {
override fun getUser(): User {
return User(1, "Tom")
}
override fun updateUser(user: User?) {
Log.d("RemoteService", "user = $user")
}
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
}
客户端调用:
val user = remoteService?.getUser()
remoteService?.updateUser(User(2, "Jerry"))
九、回调:服务端主动通知客户端
AIDL 不仅可以客户端调用服务端,也可以服务端回调客户端。
例如:服务端有数据变化时通知客户端。
1. 定义回调接口
创建:
IClientCallback.aidl
内容:
package com.example.aidldemo;
interface IClientCallback {
void onMessageReceived(String msg);
}
2. 修改服务接口
IRemoteService.aidl:
package com.example.aidldemo;
import com.example.aidldemo.IClientCallback;
interface IRemoteService {
void registerCallback(IClientCallback callback);
void unregisterCallback(IClientCallback callback);
void sendMessage(String msg);
}
3. 服务端保存回调
跨进程回调建议使用 RemoteCallbackList:
package com.example.aidldemo
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.RemoteCallbackList
import android.os.RemoteException
import android.util.Log
class RemoteService : Service() {
private val callbacks = RemoteCallbackList<IClientCallback>()
private val binder = object : IRemoteService.Stub() {
override fun registerCallback(callback: IClientCallback?) {
if (callback != null) {
callbacks.register(callback)
}
}
override fun unregisterCallback(callback: IClientCallback?) {
if (callback != null) {
callbacks.unregister(callback)
}
}
override fun sendMessage(msg: String?) {
notifyClients("Service received: $msg")
}
}
private fun notifyClients(msg: String) {
val count = callbacks.beginBroadcast()
try {
for (i in 0 until count) {
try {
callbacks.getBroadcastItem(i).onMessageReceived(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
} finally {
callbacks.finishBroadcast()
}
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun onDestroy() {
super.onDestroy()
callbacks.kill()
}
}
4. 客户端注册回调
private val callback = object : IClientCallback.Stub() {
override fun onMessageReceived(msg: String?) {
Log.d("MainActivity", "callback msg = $msg")
runOnUiThread {
// 更新 UI
}
}
}
绑定成功后:
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
remoteService = IRemoteService.Stub.asInterface(service)
try {
remoteService?.registerCallback(callback)
remoteService?.sendMessage("Hello")
} catch (e: RemoteException) {
e.printStackTrace()
}
}
解绑时:
override fun onDestroy() {
super.onDestroy()
try {
remoteService?.unregisterCallback(callback)
} catch (e: RemoteException) {
e.printStackTrace()
}
unbindService(connection)
}
十、AIDL 的线程问题
这是 AIDL 很重要的一点。
1. 服务端 AIDL 方法不一定在主线程执行
客户端调用服务端方法时,服务端的 AIDL 方法通常运行在 Binder 线程池中。
例如:
override fun sendMessage(msg: String?) {
// 这里不一定是主线程
}
所以:
- 不要直接更新 UI;
- 如果要操作主线程资源,需要切到主线程;
- 如果是耗时任务,也要做好异步处理。
2. 客户端调用可能阻塞
客户端调用:
remoteService?.getMessage()
看起来像普通方法,但其实是跨进程同步调用。
如果服务端执行很慢,客户端线程会阻塞。
所以不要在主线程调用耗时 AIDL 方法。
推荐:
lifecycleScope.launch(Dispatchers.IO) {
val result = remoteService?.add(1, 2)
withContext(Dispatchers.Main) {
// 更新 UI
}
}
十一、处理 Binder 死亡
远程进程可能被系统杀掉,这时可以监听 Binder 死亡。
private var remoteBinder: IBinder? = null
private val deathRecipient = IBinder.DeathRecipient {
remoteBinder?.unlinkToDeath(deathRecipient, 0)
remoteBinder = null
remoteService = null
Log.d("MainActivity", "Remote service died")
}
在连接成功时注册:
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
remoteBinder = service
remoteService = IRemoteService.Stub.asInterface(service)
try {
service?.linkToDeath(deathRecipient, 0)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
断开连接时:
override fun onServiceDisconnected(name: ComponentName?) {
remoteBinder?.unlinkToDeath(deathRecipient, 0)
remoteBinder = null
remoteService = null
}
十二、权限保护
如果你的 AIDL Service 暴露给其他 App,建议加权限。
Manifest 定义权限:
<permission
android:name="com.example.aidldemo.permission.BIND_REMOTE_SERVICE"
android:protectionLevel="signature" />
Service 加权限:
<service
android:name=".RemoteService"
android:exported="true"
android:permission="com.example.aidldemo.permission.BIND_REMOTE_SERVICE">
<intent-filter>
<action android:name="com.example.aidldemo.REMOTE_SERVICE" />
</intent-filter>
</service>
如果是同签名 App 才能访问,用:
android:protectionLevel="signature"
这样更安全。
十三、完整最小示例
1. IRemoteService.aidl
package com.example.aidldemo;
interface IRemoteService {
int add(int a, int b);
String getMessage();
}
2. RemoteService.kt
package com.example.aidldemo
import android.app.Service
import android.content.Intent
import android.os.IBinder
class RemoteService : Service() {
private val binder = object : IRemoteService.Stub() {
override fun add(a: Int, b: Int): Int {
return a + b
}
override fun getMessage(): String {
return "Hello AIDL"
}
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
}
3. AndroidManifest.xml
<service
android:name=".RemoteService"
android:process=":remote"
android:exported="false" />
4. MainActivity.kt
package com.example.aidldemo
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private var remoteService: IRemoteService? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
remoteService = IRemoteService.Stub.asInterface(service)
try {
val result = remoteService?.add(10, 20)
val message = remoteService?.getMessage()
Log.d("AIDL", "result = $result")
Log.d("AIDL", "message = $message")
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
remoteService = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent(this, RemoteService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection)
}
}
十四、常见问题
1. 找不到 IRemoteService?
检查:
src/main/aidl/包名/IRemoteService.aidl
目录、包名必须一致。
修改 AIDL 后可以:
Build -> Rebuild Project
2. 跨 App 调用失败?
检查:
- 服务端
android:exported="true"; - 客户端 AIDL 文件包名和内容一致;
Intent是否设置了服务端 package;- 服务端 App 是否已安装;
- 是否有权限限制。
3. AIDL 方法里能不能传普通 data class?
不能直接传。
要么:
- 使用基础类型;
- 使用 Parcelable;
- 使用 List/Map;
- 或者拆成多个基础参数。
4. AIDL 和 Messenger 怎么选?
| 方案 | 特点 |
|---|---|
| AIDL | 适合复杂接口、多方法、高性能、双向通信 |
| Messenger | 基于 Handler,适合简单消息传递 |
| Broadcast | 一对多通知,不适合强交互 |
| ContentProvider | 适合结构化数据共享 |
| Socket | 跨设备或特殊通信场景 |
十五、使用 AIDL 的建议
实际开发中建议:
- AIDL 接口保持简单;
- 不要在 AIDL 方法中做耗时操作;
- 客户端避免主线程调用耗时远程方法;
- 自定义对象使用 Parcelable;
- 跨 App 暴露服务时加权限;
- 回调使用
RemoteCallbackList; - 处理
RemoteException; - 需要考虑远程进程死亡。
一句话总结:
AIDL 就是先定义
.aidl接口,Service 实现Stub,客户端通过Stub.asInterface()拿到远程对象,然后像调用普通方法一样进行跨进程调用。