AIDL 安卓

下面按「从 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 接口

核心流程:

  1. 定义 .aidl 接口文件;
  2. 编译后生成 Java Binder 接口;
  3. Service 实现 Stub
  4. 客户端通过 Stub.asInterface(IBinder) 拿到远程接口;
  5. 像调用普通方法一样调用远程方法。

二、创建 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 默认支持一些基础类型,比如:
    • int
    • long
    • float
    • double
    • boolean
    • String
    • CharSequence
    • List
    • Map
    • 实现 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 的建议

实际开发中建议:

  1. AIDL 接口保持简单;
  2. 不要在 AIDL 方法中做耗时操作;
  3. 客户端避免主线程调用耗时远程方法;
  4. 自定义对象使用 Parcelable;
  5. 跨 App 暴露服务时加权限;
  6. 回调使用 RemoteCallbackList
  7. 处理 RemoteException
  8. 需要考虑远程进程死亡。

一句话总结:

AIDL 就是先定义 .aidl 接口,Service 实现 Stub,客户端通过 Stub.asInterface() 拿到远程对象,然后像调用普通方法一样进行跨进程调用。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇