前言
介绍下工作中一个有意思的跨进程SDK,其原理是通过ContentProvider+Binder来实现跨进程通信的封装,且客户端与服务提供端的接入成本较低。
客户端在发起调用时仅需要几行代码即可实现跨进程通信调用。
kotlin
val methodName = "methodName"
val params = "{\"key\":\"value\"}"
val res = apiClientImpl.call(methodName, params) Log.d(TAG, "API call result: $res")
客户端不知道服务端是谁,服务端也不知道自己的客户端是谁,全由SDK来充当服务发现者,颇有SOA架构的味道。
周末抽空简单复现了下实现。
前置要求
客户端
实现ApiClient
创建继承自SDK库中的 ApiClient
的实现类:
kotlin
class ApiClientImpl(val activityContext: Context) : ApiClient() {
override fun getModuleName(): String {
return "demo"
}
override fun getAppId(): String {
return "server"
}
override fun getContext(): Context {
return activityContext
}
}
- getAppId()是用于获取服务应用的包名。
- getModuleName()是识别获取服务应用的服务名。
- getContext()是用来获取服务端的ContentProvider。
服务端
1. 注册ContentProvider
在服务端的 AndroidManifest.xml
中注册SDK中的ApiPublisher:
xml
<provider
android:name="com.ben.ipc.ApiPublisher"
android:authorities="com.ben.server.provider"
android:enabled="true"
android:exported="true" />
2. 实现服务逻辑
创建实现 IpcServerListener
接口的服务类:
kotlin
object ServerImplementation : IpcServerListener {
val TAG = ServerImplementation::class.simpleName
fun init() {
Log.d(TAG, "ServerImplementation initialized")
RemoteApiImpl.listener = this
}
override fun onCall(
moduleName: String,
method: String?,
arg: String?
): String {
Log.d(TAG, "onCall moduleName: $moduleName, method: $method, arg: $arg")
return "Invokes method $method with arg $arg in module $moduleName"
}
}
SDK实现
SDK采用了分层架构设计,通过AIDL和ContentProvider实现跨进程通信。以下是各个核心组件的实现细节:
1. 核心接口层
AIDL接口定义
定义了跨进程通信的标准接口:
aidl
interface IRemoteApi {
String onCall(String moduleName, String method, String param);
}
这个接口采用了通用的方法调用模式,通过 moduleName
、method
和 param
三个参数实现灵活的服务调用。
- moduleName是用于区分同一服务端的不同服务类或服务模块。
- method是要调用具体的方法。
- param的参数。
2. 客户端实现层
ApiClient抽象基类
提供了客户端的抽象接口:
kotlin
abstract class ApiClient {
abstract fun getModuleName(): String
abstract fun getAppId(): String
abstract fun getContext(): Context
fun call(methodName: String, paras: String): String {
return ApiManager.call(getAppId(), getModuleName(), methodName, paras, getContext())
}
}
设计特点:
- 抽象化客户端配置信息(模块名、应用ID、上下文)
- 提供统一的调用入口
call()
方法 - 将具体的路由逻辑委托给
ApiManager
ApiManager服务管理器
负责应用ID到包名的映射和路由:
kotlin
object ApiManager {
private val appIdToPackageId = mutableMapOf<String, String>().apply {
put("server", "com.ben.server")
}
fun call(appId: String, moduleName: String, method: String, params: String, context: Context): String {
val packageId = appIdToPackageId[appId]
?: throw IllegalArgumentException("No package ID found for app ID: $appId")
return Router.route(packageId, moduleName, method, params, context)
}
}
核心功能:
- 维护应用ID与包名的映射关系
- 提供服务发现机制
- 参数验证和错误处理
Router路由器
实现具体的跨进程通信逻辑:
kotlin
object Router {
fun route(packageId: String, moduleName: String, method: String, arg: String?, context: Context): String {
val builder = Uri.Builder()
builder.scheme("content").authority(packageId + "." + ApiPublisher::class.simpleName)
val uri = builder.build()
val contentResolver = context.contentResolver
val contentProviderClient = contentResolver.acquireContentProviderClient(uri)
if (contentProviderClient != null) {
val bundle = contentProviderClient.call(method, arg, null)
val binder = bundle?.getBinder("binder")
val remoteApiImpl = IRemoteApi.Stub.asInterface(binder)
val res = remoteApiImpl.onCall(moduleName, method, arg)
contentProviderClient.release()
return res
}
return "-1"
}
}
实现细节:
- 构建ContentProvider的URI(格式:
content://{packageId}.ApiPublisher
) - 通过ContentResolver获取远程ContentProvider
- 调用ContentProvider的
call()
方法获取IBinder - 将IBinder转换为AIDL接口代理
- 执行远程方法调用并返回结果
3. 服务端实现层
ApiPublisher服务发布者
作为ContentProvider暴露服务:
kotlin
class ApiPublisher : ContentProvider() {
private val remoteApi: IBinder = RemoteApiImpl
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
val res = Bundle()
res.putBinder("binder", remoteApi)
return res
}
}
核心功能:
- 继承ContentProvider提供跨进程访问能力
- 在
call()
方法中返回IBinder对象 - 作为服务注册中心的角色
RemoteApiImpl服务实现
实现AIDL接口:
kotlin
object RemoteApiImpl : IRemoteApi.Stub() {
var listener: IpcServerListener? = null
override fun onCall(moduleName: String, method: String?, param: String?): String {
return listener?.onCall(moduleName, method, param) ?: "No listener set"
}
}
设计模式:
- 采用委托模式,将具体业务逻辑委托给
IpcServerListener
- 作为AIDL接口的具体实现,处理跨进程调用
- 使用单例模式确保全局唯一
IpcServerListener业务接口
定义服务端业务接口:
kotlin
interface IpcServerListener {
fun onCall(moduleName: String, method: String?, arg: String?): String
}
5. SDK架构优势
分层解耦
- 接口层:AIDL定义标准通信协议
- 客户端层:ApiClient、ApiManager、Router处理客户端逻辑
- 服务端层:ApiPublisher、RemoteApiImpl处理服务端逻辑
- 业务层:IpcServerListener处理具体业务逻辑
扩展性设计
- 服务发现:通过ApiManager的映射表支持多服务注册
- 协议统一:通过AIDL接口统一通信协议
- 业务解耦:通过监听器模式分离框架和业务代码
通信流程
- 客户端调用 → ApiClient.call()
- 服务发现 → ApiManager查找目标包名
- 路由转发 → Router构建URI并获取ContentProvider
- 获取服务 → ContentProvider返回IBinder
- 远程调用 → 通过AIDL代理调用远程方法
- 业务处理 → RemoteApiImpl委托给IpcServerListener
- 结果返回 → 沿原路径返回调用结果
这种设计实现了客户端和服务端的完全解耦,支持动态服务发现,具有良好的可扩展性和维护性。
写在最后
上述的代码是简化了的实现,是存在可优化点的,如缓存Binder实例,如在SDK侧进行方法名的校验等等。
有错误欢迎指出。