安卓AIDL跨应用通讯的实现
一、 服务端的实现
- 在项目的app/src/main 目录下创建 aidl 文件夹,目录结构应该与您的Java包结构一致,例如:app/src/main/aidl/com/hfyang/applicationtest/。
- 在app的build.gradle中添加aidl配置。
buildFeatures {aidl true}sourceSets {main {aidl.srcDirs = ['src/main/aidl']}}
- 创建IPartnerService.aidl接口。
package com.hfyang.applicationtest;import com.hfyang.applicationtest.PartnerData;
import com.hfyang.applicationtest.IPartnerCallback;// 主要服务接口
interface IPartnerService {// 注册回调void registerCallback(IPartnerCallback callback);// 注销回调void unregisterCallback(IPartnerCallback callback);// 发送数据到服务端void sendData(in PartnerData data);// 获取服务状态boolean isServiceConnected();// 执行远程操作String executeCommand(String command, String params);
}
- 创建IPartnerCallback.aidl接口。
package com.hfyang.applicationtest;import com.hfyang.applicationtest.PartnerData;// 回调接口
interface IPartnerCallback {void onDataReceived(in PartnerData data);void onError(String error);
}
- 创建PartnerData.aidl用于包装数据,AIDL 只需声明 parcelable PartnerData;,真正的 Parcelable 类只在服务端实现即可。
注:src/main/aidl 目录只给 .aidl 文件用,放 Kotlin/Java 类不会被编译,也不会参与打包。
package com.hfyang.applicationtest;// Parcelable类声明
parcelable PartnerData;
- 创建Parcelable类PartnerData的具体实现。
package com.hfyang.applicationtestimport android.os.Parcel
import android.os.Parcelabledata class PartnerData(var id: String = "",var message: String = "",var timestamp: Long = 0L,var data: Map<String, Any>? = null
) : Parcelable {constructor(parcel: Parcel) : this(parcel.readString() ?: "",parcel.readString() ?: "",parcel.readLong(),parcel.readHashMap(String::class.java.classLoader) as? Map<String, Any>)override fun writeToParcel(parcel: Parcel, flags: Int) {parcel.writeString(id)parcel.writeString(message)parcel.writeLong(timestamp)parcel.writeMap(data)}override fun describeContents(): Int = 0companion object CREATOR : Parcelable.Creator<PartnerData> {override fun createFromParcel(parcel: Parcel): PartnerData = PartnerData(parcel)override fun newArray(size: Int): Array<PartnerData?> = arrayOfNulls(size)}
}
上述创建完成后,执行Rebuild即可在build/generated/aidl_source_output_dir下生成对应的文件。
- 服务端PartnerService的实现。
package com.hfyang.applicationtestimport android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.RemoteCallbackList
import android.os.RemoteException
import android.util.Log
import java.util.concurrent.Executorsclass PartnerService : Service() {private val TAG = "PartnerService"private val callbacks = RemoteCallbackList<IPartnerCallback>()private var isConnected = falseprivate val binder = object : IPartnerService.Stub() {override fun registerCallback(callback: IPartnerCallback?) {callbacks.register(callback)isConnected = true}override fun unregisterCallback(callback: IPartnerCallback?) {callbacks.unregister(callback)if (callbacks.registeredCallbackCount == 0) {isConnected = false}}override fun sendData(data: PartnerData?) {// 处理接收到的数据data?.let {// 通知所有注册的回调notifyCallbacksAsync(it)processReceivedData(it)}}override fun isServiceConnected(): Boolean = isConnectedoverride fun executeCommand(command: String?, params: String?): String {return when (command) {"getStatus" -> "Service is running""getData" -> "Data from service"else -> "Unknown command"}}}override fun onBind(intent: Intent?): IBinder = binderprivate fun processReceivedData(data: PartnerData) {// 处理业务逻辑println("Received data: ${data.message}")Log.d(TAG, "processReceivedData: $data")}private fun notifyCallbacksAsync(data: PartnerData) {// 使用线程池执行通知操作,避免阻塞主线程Executors.newSingleThreadExecutor().submit {notifyCallbacks(data)}}private fun notifyCallbacks(data: PartnerData) {val count = callbacks.beginBroadcast()try {for (i in 0 until count) {try {callbacks.getBroadcastItem(i).onDataReceived(data)} catch (e: RemoteException) {e.printStackTrace()}}} finally {callbacks.finishBroadcast()}}override fun onDestroy() {super.onDestroy()callbacks.kill()}
}
- 在AndroidManifest.xml中注册Service声名。
<serviceandroid:name=".PartnerService"android:enabled="true"android:exported="true"android:process=":remote"><intent-filter><action android:name="com.hfyang.applicationtest.PartnerService" /></intent-filter></service>
二、客户端的实现
- 将服务端的aidl文件及其路径复制一份到客户端的main后,执行Rebuild,生成对应的文件。此时生成的文件会找不到com.hfyang.applicationtest.PartnerData这个具体的实现类,只需在java目录下创建一个即可。
如果需要,亦可将aidl部分直接打包成aar使用,也不会存在该问题。
- 客户端PartnerClient的实现。
package com.hfyang.clientimport android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.os.RemoteException
import com.hfyang.applicationtest.IPartnerCallback
import com.hfyang.applicationtest.IPartnerService
import com.hfyang.applicationtest.PartnerData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launchclass PartnerClient(private val context: Context) {private var remote: IPartnerService? = null@Volatile var isConnected = falseprivate setprivate val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())private val connection = object : ServiceConnection {override fun onServiceConnected(name: ComponentName?, service: IBinder?) {remote = IPartnerService.Stub.asInterface(service)isConnected = truetry { remote?.registerCallback(callback) } catch (_: RemoteException) { }onConnected?.invoke()}override fun onServiceDisconnected(name: ComponentName?) {isConnected = falseremote = nullonDisconnected?.invoke()}}private val callback = object : IPartnerCallback.Stub() {override fun onDataReceived(data: PartnerData?) {if (data != null) coroutineScope.launch { onData?.invoke(data) }}override fun onError(error: String?) {if (error != null) coroutineScope.launch { onError?.invoke(error) }}}var onConnected: (() -> Unit)? = nullvar onDisconnected: (() -> Unit)? = nullvar onData: ((PartnerData) -> Unit)? = nullvar onError: ((String) -> Unit)? = nullfun bindByAction() {val intent = Intent("com.hfyang.applicationtest.PartnerService").apply {setPackage("com.hfyang.applicationtest") // 服务端包名}context.bindService(intent, connection, Context.BIND_AUTO_CREATE)}fun bindByComponent() {val intent = Intent().apply {component = ComponentName("com.hfyang.applicationtest","com.hfyang.applicationtest.PartnerService")}context.bindService(intent, connection, Context.BIND_AUTO_CREATE)}fun unbind() {try { remote?.unregisterCallback(callback) } catch (_: RemoteException) { }runCatching { context.unbindService(connection) }isConnected = falseremote = nullcoroutineScope.cancel()}fun send(data: PartnerData) {if (!isConnected) returntry { remote?.sendData(data) } catch (_: RemoteException) { }}fun exec(command: String, params: String = ""): String? {if (!isConnected) return nullreturn try {remote?.executeCommand(command, params)} catch (_: RemoteException) {null}}
}
- 在AndroidManifest.xml的注册queries。
<queries><!-- 目标服务端应用包名 --><package android:name="com.hfyang.applicationtest" /><!-- 或按组件可见性声明 --><intent><action android:name="com.hfyang.applicationtest.PartnerService" /></intent></queries>
- 在Activity中实现连接。
private val TAG = "MainActivity"private lateinit var partnerClient: PartnerClientoverride fun onCreate(savedInstanceState: Bundle?) {partnerClient = PartnerClient(this).apply {onConnected = {// 连接成功后发送一条消息send(PartnerData(id = "1",message = "hello",timestamp = System.currentTimeMillis()))}onData = { data ->Log.d(TAG, "processReceivedData: $data")}onError = { err ->// 处理错误}}// 二选一:按 action 绑定或按 component 绑定
// partnerClient.bindByAction()partnerClient.bindByComponent()}override fun onDestroy() {super.onDestroy()partnerClient.unbind()}
三、注意事项
- 禁止跨进程 UI 操作:服务端Service 进程仅负责数据处理,主进程通过回调接收结果后,必须用 runOnUiThread 或 Handler(Looper.getMainLooper()) 确保 UI 操作在主线程执行。
- 避免直接依赖:服务端Service 与主进程通过明确定义的 IPC 协议通信,杜绝硬编码 UI 逻辑到 Service 中。
- 避免阻塞 Binder 线程:客户端AIDL 回调方法(onDataReceived/onError)应快速执行,禁止在其中执行耗时操作(如网络请求、数据库写入),否则会阻塞 Binder 线程池。
- 线程安全:客户端即使切换到主线程,仍需注意共享数据的线程安全(如使用 @Synchronized 或 Lock 处理多线程读写)。
- 内存泄漏防护:客户端无论使用 Handler 还是协程,都需在 unbind() 时清理资源(如 Handler.removeCallbacksAndMessages(null) 或协程取消)。