Android跨进程通信(双向通信包含in-out-inout)

  • 客户端 → 服务端:请求数据

  • 服务端 → 客户端:返回结果

  • 服务端 → 客户端:通过回调接口主动通知(真正的双向通信)

🧩 一、AIDL 文件结构

代码

aidl/
 ├── IRemoteService.aidl
 └── IRemoteCallback.aidl

🧭 二、定义回调接口(客户端实现,服务端调用)

IRemoteCallback.aidl

aidl

package com.demo.ipc;

interface IRemoteCallback {
    void onResult(int code, String message);
}

🧭 三、定义服务端接口(客户端调用,服务端实现)

IRemoteService.aidl

aidl

package com.demo.ipc;

import com.demo.ipc.IRemoteCallback;

interface IRemoteService {

    // 普通请求:客户端 → 服务端
    int add(int a, int b);

    // 注册回调:客户端把 callback 传给服务端
    void registerCallback(IRemoteCallback callback);

    // 注销回调
    void unregisterCallback(IRemoteCallback callback);
}

🧩 四、服务端实现(Service + Binder)

RemoteService.kt

kotlin

class RemoteService : Service() {

    private val callbacks = RemoteCallbackList<IRemoteCallback>()

    private val binder = object : IRemoteService.Stub() {

        override fun add(a: Int, b: Int): Int {
            return a + b
        }

        override fun registerCallback(callback: IRemoteCallback?) {
            if (callback != null) {
                callbacks.register(callback)
            }
        }

        override fun unregisterCallback(callback: IRemoteCallback?) {
            if (callback != null) {
                callbacks.unregister(callback)
            }
        }
    }

    override fun onBind(intent: Intent?): IBinder = binder

    // 服务端主动回调客户端
    fun notifyClients() {
        val count = callbacks.beginBroadcast()
        for (i in 0 until count) {
            callbacks.getBroadcastItem(i).onResult(200, "服务端主动推送消息")
        }
        callbacks.finishBroadcast()
    }
}

🧠 服务端关键点说明

  • RemoteCallbackList 用于管理跨进程回调(自动处理死亡代理)
  • registerCallback() / unregisterCallback() 用于客户端注册监听
  • notifyClients() 是服务端主动推送消息给客户端的地方

你可以在定时器、网络回调、业务事件中调用它。

🧩 五、客户端实现(绑定服务 + 注册回调)

ClientActivity.kt

kotlin

class ClientActivity : AppCompatActivity() {

    private var remoteService: IRemoteService? = null

    private val callback = object : IRemoteCallback.Stub() {
        override fun onResult(code: Int, message: String?) {
            Log.d("Client", "收到服务端回调: code=$code, msg=$message")
        }
    }

    private val conn = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            remoteService = IRemoteService.Stub.asInterface(service)

            // 注册回调
            remoteService?.registerCallback(callback)

            // 调用服务端方法
            val result = remoteService?.add(3, 5)
            Log.d("Client", "add(3,5) = $result")
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            remoteService = null
        }
    }

    override fun onStart() {
        super.onStart()
        val intent = Intent()
        intent.setClassName("com.demo.ipcserver", "com.demo.ipc.RemoteService")
        bindService(intent, conn, BIND_AUTO_CREATE)
    }

    override fun onStop() {
        super.onStop()
        remoteService?.unregisterCallback(callback)
        unbindService(conn)
    }
}

🧠 客户端关键点说明

  • IRemoteCallback.Stub() 是客户端实现的回调接口
  • registerCallback() 把回调传给服务端
  • 服务端调用 onResult() 时,客户端会立即收到消息
  • add() 是普通同步 IPC 调用

🔥 六、Demo 的双向通信流程图

代码

客户端 ----------------------> 服务端
          add(a,b)

客户端 ----------------------> 服务端
       registerCallback(callback)

服务端 ----------------------> 客户端
       callback.onResult(...)

这是典型的:

  • 请求 → 响应
  • 服务端主动推送

双向通信完整闭环。

🧩 七、AIDL 的 in / out / inout 关键字

  • in:只读输入
  • out:只写输出(客户端传入的对象会被服务端“填充”)
  • inout:读 + 写(客户端传入对象 → 服务端修改 → 回传)

你不需要额外定义一个“返回模型”,除非你想让输入和输出完全不同。

🧩 为什么 90% 场景用 in

因为:

  • 大多数参数只是输入
  • in 性能最好(只序列化一次)
  • 逻辑最简单
  • 不需要回传修改后的对象

例如:

aidl

void updateUser(in User user);

服务端修改 user 不会回传给客户端。

🧠 那么如果我需要返回数据怎么办?

你有两种选择:

✔ 方案 1:定义一个返回值(最常用)

例如:

aidl

User getUser(int id);

这是最清晰、最常见的方式。

客户端:

kotlin

val user = service.getUser(1)

✔ 方案 2:使用 outinout(复用同一个对象)

例如:

aidl

void getUser(out User user);

客户端:

kotlin

User user = new User(); // 空对象
service.getUser(user);
// user 现在被服务端填充了

服务端:

java

void getUser(User user) {
    user.id = 1;
    user.name = "Tom";
}

注意:客户端传入的 user 对象会被服务端“写回”数据。

🔥 重点:outinout 是否可以复用客户端传入的对象?

答案是:

可以,而且这是它们的设计目的。

out:只回传,不读取输入

客户端传入的对象内容会被忽略,但对象本身会被用来“承载返回值”。

inout:既读取输入,也回传修改后的内容

客户端传入的对象 → 服务端修改 → 回传给客户端。

🧭 什么时候用 out / inout

场景 推荐关键字
只需要返回数据 out
需要传入对象并让服务端修改后回传 inout
只需要传入,不需要回传 in(默认)

🎯 最终总结(非常关键)

✔ 你不需要额外定义一个“返回模型”

使用 outinout 时:

  • 客户端传入的对象本身就是“返回容器”
  • 服务端直接写入这个对象
  • Binder 会把修改后的对象序列化回客户端

✔ 但实际工程中,推荐使用“返回值”方式

因为:

  • 更清晰
  • 更符合直觉
  • 更容易维护
  • 更少序列化开销

例如:

aidl

User getUser(int id);
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容