【架构安全修复 - 防止协程泄漏和内存泄漏】
## 问题背景
TssRepository 原有 4 个独立的 Job 变量:
- messageCollectionJob: 消息路由任务
- sessionEventJob: 会话事件订阅任务
- sessionStatusPollingJob: 会话状态轮询任务
- progressCollectionJob: 进度收集任务
每个 Job 需要手动取消,容易在以下场景导致协程泄漏:
1. Activity 销毁时某个 Job 忘记取消 → 后台协程继续运行 → 内存泄漏 → OOM
2. 快速重启连接时旧 Job 未取消 → 多个 Job 并行运行 → 资源竞争
3. 异常路径中某个 Job 未取消 → 僵尸协程 → 内存累积
## 修复方案
### 1. 创建 JobManager 统一管理类
```kotlin
private inner class JobManager {
private val jobs = mutableMapOf<String, Job>()
fun launch(name: String, block: suspend CoroutineScope.() -> Unit): Job {
jobs[name]?.cancel() // 自动取消同名旧 Job
val job = repositoryScope.launch(block = block)
jobs[name] = job
return job
}
fun cancel(name: String) { ... }
fun isActive(name: String): Boolean { ... }
fun cancelAll() { ... } // 一键清理所有 Job
}
```
### 2. 定义 Job 名称常量
```kotlin
companion object {
const val JOB_MESSAGE_COLLECTION = "message_collection"
const val JOB_SESSION_EVENT = "session_event"
const val JOB_SESSION_STATUS_POLLING = "session_status_polling"
const val JOB_PROGRESS_COLLECTION = "progress_collection"
}
```
### 3. 迁移所有 Job 使用方式
**启动 Job:**
```kotlin
// BEFORE:
messageCollectionJob?.cancel()
messageCollectionJob = repositoryScope.launch { ... }
// AFTER:
jobManager.launch(JOB_MESSAGE_COLLECTION) { ... }
// 自动取消旧 Job,无需手动 cancel
```
**取消 Job:**
```kotlin
// BEFORE:
messageCollectionJob?.cancel()
// AFTER:
jobManager.cancel(JOB_MESSAGE_COLLECTION)
```
**检查 Job 状态:**
```kotlin
// BEFORE:
if (messageCollectionJob == null || messageCollectionJob?.isActive != true)
// AFTER:
if (!jobManager.isActive(JOB_MESSAGE_COLLECTION))
```
**清理所有 Job:**
```kotlin
// BEFORE (需要手动取消每个 Job,容易遗漏):
fun cleanup() {
messageCollectionJob?.cancel()
sessionEventJob?.cancel()
sessionStatusPollingJob?.cancel()
progressCollectionJob?.cancel() // 如果漏了这个 → 内存泄漏
repositoryScope.cancel()
}
// AFTER (一键清理,永不遗漏):
fun cleanup() {
jobManager.cancelAll()
repositoryScope.cancel()
}
```
## 修复的崩溃场景
### 场景 1: Activity 快速销毁重建
- **原问题**: Activity 销毁时如果某个 Job 未取消,后台协程继续持有 Activity/Context 引用
- **后果**: 内存泄漏,多次重建后 OOM 崩溃
- **修复**: JobManager.cancelAll() 确保所有 Job 都被取消
### 场景 2: 网络重连时资源竞争
- **原问题**: disconnect() 后 reconnect() 启动新 Job,但旧 Job 未取消
- **后果**: 多个 messageCollectionJob 并行运行,消息重复处理,状态混乱
- **修复**: JobManager.launch() 自动取消同名旧 Job
### 场景 3: 异常路径中 Job 未清理
- **原问题**: try-catch 中异常发生后,cleanup 逻辑被跳过
- **后果**: 僵尸协程累积,内存持续增长
- **修复**: JobManager 集中管理,即使部分清理失败,cancelAll() 仍能清理全部
## 影响范围
### 修改的函数 (共 11 个):
1. disconnect() - 使用 jobManager.cancelAll()
2. cleanup() - 使用 jobManager.cancelAll()
3. startSessionEventSubscription() - 使用 jobManager.launch(JOB_SESSION_EVENT)
4. ensureSessionEventSubscriptionActive() - 使用 jobManager.isActive(JOB_SESSION_EVENT)
5. startProgressCollection() - 使用 jobManager.launch(JOB_PROGRESS_COLLECTION)
6. stopProgressCollection() - 使用 jobManager.cancel(JOB_PROGRESS_COLLECTION)
7. startSessionStatusPolling() - 使用 jobManager.launch(JOB_SESSION_STATUS_POLLING)
8. stopSessionStatusPolling() - 使用 jobManager.cancel(JOB_SESSION_STATUS_POLLING)
9. startMessageRouting() - 使用 jobManager.launch(JOB_MESSAGE_COLLECTION)
10. cancelSession() - 使用 jobManager.cancel() 取消多个 Job
11. 多个签名/密钥生成完成后的清理逻辑 - 使用 jobManager.cancel(JOB_MESSAGE_COLLECTION)
### 删除的变量:
- messageCollectionJob: Job?
- sessionEventJob: Job?
- sessionStatusPollingJob: Job?
- progressCollectionJob: Job?
### 新增代码:
- JobManager 内部类 (110 行,含详细注释)
- 4 个 Job 名称常量
## 测试验证
编译状态: ✅ BUILD SUCCESSFUL in 2m 10s
- 无编译错误
- 仅有警告 (unused parameters),不影响功能
## 后续优化建议
可以进一步优化:
1. 添加 Job 超时检测 (避免永久运行的僵尸协程)
2. 添加 Job 异常处理回调 (统一的错误处理)
3. 添加 Job 启动/取消日志 (已在 JobManager 中实现)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
||
|---|---|---|
| .claude | ||
| backend | ||
| contracts | ||
| docs | ||
| frontend | ||
| kubernetes | ||
| scripts | ||
| tests | ||
| .gitignore | ||
| README.md | ||
| SEED01-qrcode.png | ||
| STKAITI.TTF | ||
| contract.docx | ||
| docker-compose.yml | ||
| 挖矿.xlsx | ||
| 榴莲皇后数据.xlsx | ||
| 联合种植协议董事长_release_form.pdf | ||