桥接层架构:创建 Bridge 接口、定义支付请求与响应数据类、线程调度抽象
4.1 桥接层的作用与设计原则
在支付宝支付 SDK 与 Jetpack Compose 之间加入桥接层,是为了将原生 SDK 的同步 / 回调式 API 转化为 Compose 可组合函数能够安全调用的异步、无副作用接口。桥接层承担三个职责:
- 接口统一:屏蔽支付宝 SDK 不同版本的 API 差异,对外暴露简洁的
PaymentBridge接口。 - 数据封装:将原始
Map<String, String>或回调参数转换为强类型数据类,便于 Compose 状态管理。 - 线程调度:保证支付请求在 IO 线程执行,结果回调自动切换到主线程,避免 Compose 重组线程冲突。
桥接层本身不应包含任何业务逻辑(如订单校验、签名计算),只负责协议转换与线程安全。业务逻辑应放在 ViewModel 或 UseCase 层。
4.2 Bridge 接口定义
接口围绕支付生命周期设计,包含发起支付、查询支付结果、取消支付三个核心方法。
// PaymentBridge.kt
interface PaymentBridge {
suspend fun pay(request: PaymentRequest): PaymentResult
suspend fun queryPaymentResult(orderId: String): PaymentResult
fun cancelPayment(orderId: String)
}
pay为挂起函数,调用后会启动支付宝 SDK 支付流程,并在支付完成后返回结果。queryPaymentResult用于在异常断网或回调未收到时主动轮询。cancelPayment为普通函数,用于在 UI 层主动取消等待中的支付。
4.3 支付请求与响应数据类
数据类将支付宝 SDK 的 Map / Bundle 参数和回调结果映射为不可变对象,便于在 Compose 中作为状态使用。
| 数据类 | 字段 | 说明 |
|---|---|---|
PaymentRequest | val orderInfo: String | 服务端签名的订单信息字符串 |
val appScheme: String | 用于跳转回应用的 URL Scheme | |
PaymentResult | val resultStatus: Int | 9000 成功,6001 取消,6002 网络错误等 |
val memo: String | 结果说明文字 | |
val result: String? | 同步返回的完整结果 JSON(可选) | |
PaymentError | val errorCode: Int | 自定义错误码(-1 未初始化,-2 参数错误) |
val message: String | 人类可读的错误描述 |
// PaymentRequest.kt
data class PaymentRequest(
val orderInfo: String,
val appScheme: String = "alipays"
)
// PaymentResult.kt
data class PaymentResult(
val resultStatus: Int,
val memo: String,
val result: String? = null
)
// PaymentError.kt
data class PaymentError(
val errorCode: Int,
val message: String
)
支付宝 SDK 的
resultStatus 为 9000 仅代表支付成功,但仍需对 result 字段做签名校验。桥接层不负责签名验证,但应保留原始 result 字符串供上层校验。4.4 线程调度抽象
为了保证 Compose 状态更新的线程安全,桥接层必须提供线程调度抽象。推荐使用 Kotlin 协程的 Dispatchers,但为了便于单元测试和切换实现,定义 PaymentDispatcher 接口。
// PaymentDispatcher.kt
interface PaymentDispatcher {
val io: CoroutineDispatcher
val main: CoroutineDispatcher
}
class DefaultPaymentDispatcher : PaymentDispatcher {
override val io: CoroutineDispatcher = Dispatchers.IO
override val main: CoroutineDispatcher = Dispatchers.Main
}
在 Bridge 实现类中,通过依赖注入接收 PaymentDispatcher,并使用 withContext 切换调度器:
class AlipayBridgeImpl(
private val dispatcher: PaymentDispatcher = DefaultPaymentDispatcher()
) : PaymentBridge {
override suspend fun pay(request: PaymentRequest): PaymentResult = withContext(dispatcher.io) {
// 调用支付宝 SDK 同步方法(阻塞 IO 线程)
val resultMap = AlipayService.payV2(request.orderInfo, request.appScheme)
withContext(dispatcher.main) {
PaymentResult(
resultStatus = resultMap["resultStatus"]?.toIntOrNull() ?: -1,
memo = resultMap["memo"] ?: "",
result = resultMap["result"]
)
}
}
// ... 其他方法类似
}
4.5 桥接层架构总览
4.6 实现流程(逐步指南)
- 创建
bridge包,包含接口、数据类、调度器三个文件。 - 定义
PaymentBridge接口,方法签名如上。 - 定义
PaymentRequest、PaymentResult、PaymentError数据类。 - 定义
PaymentDispatcher接口及默认实现。 - 创建实现类
AlipayBridgeImpl,注入PaymentDispatcher,在每个方法中使用withContext(dispatcher.io)包裹支付宝 SDK 调用,并在返回前切回dispatcher.main。 - 在
Application或依赖注入模块中提供PaymentBridge的单例实例。
4.7 调试与常见错误
调试点:在
AlipayBridgeImpl 的 pay 方法入口和出口添加日志,打印线程名称和 resultStatus,确认调度器正常工作。- 错误:Context 泄漏 — 如果
AlipayBridgeImpl持有 Activity 引用,会导致内存泄漏。解决:只传递 Application Context。 - 错误:主线程卡死 — 支付宝 SDK 的
payV2是同步方法,如果在主线程调用会 ANR。确保使用withContext(dispatcher.io)。 - 错误:Compose 重组异常 — 如果在非主线程更新 Compose 状态,会抛出
CalledFromWrongThreadException。解决:始终通过dispatcher.main返回结果。
4.8 性能与稳定性优化
| 优化目标 | 措施 |
|---|---|
| 减少线程切换开销 | 支付宝 SDK 本身可能异步回调,可在回调内部直接切线程,避免多次调度 |
| 结果缓存 | 对 queryPaymentResult 添加短期内存缓存(5秒),避免重复请求 |
| 取消支持 | 在 cancelPayment 中设置 AtomicBoolean 标志,pay 方法内轮询该标志并尽早返回 |
| 自定义调度器 | 对于高并发场景,可提供 LimitedParallelismDispatcher 限制并发数 |
稳定性最关键的实践:在
AlipayBridgeImpl 内将所有支付宝 SDK 原生异常捕获为 PaymentError 并抛出,而不是让上层直接捕获平台异常。这样可以做到错误类型统一。