404 lines
12 KiB
Markdown
404 lines
12 KiB
Markdown
# 函数调用完整分析报告
|
|
|
|
基于对代码的全面搜索,以下是各核心函数的调用情况分析。
|
|
|
|
---
|
|
|
|
## 一、备份功能调用链
|
|
|
|
### 1. **顶层入口函数**
|
|
```
|
|
BackupViewModel.executeBackup() (UI 触发)
|
|
↓
|
|
BackupOperation.backupApps() (核心备份)
|
|
↓
|
|
├── backupUserData() (用户数据)
|
|
├── backupObb() (OBB 数据)
|
|
├── backupExternalData() (外部数据)
|
|
├── backupSsaid() (SSAID)
|
|
├── backupPermissions() (权限)
|
|
├── writeFileForBackup() (元数据)
|
|
├── readTextFile() (读取旧元数据)
|
|
└── buildAppDetailsJson() (构建 JSON)
|
|
```
|
|
|
|
### 2. **BackupOperation 核心函数调用统计**
|
|
|
|
| 函数 | 调用次数 | 主要调用者 |
|
|
|------|---------|-----------|
|
|
| `backupApps()` | 1 | BackupViewModel:199 |
|
|
| `backupUserData()` | 1 | BackupOperation:225 |
|
|
| `backupObb()` | 1 | BackupOperation:243 |
|
|
| `backupExternalData()` | 1 | BackupOperation:256 |
|
|
| `backupSsaid()` | 1 | BackupOperation:262 |
|
|
| `backupPermissions()` | 1 | BackupOperation:267 |
|
|
| `writeFileForBackup()` | 8 | BackupOperation(6), ResticStreamBackup(2) |
|
|
| `readTextFile()` | 6 | BackupOperation(3), RestoreScreen(2), RestoreOperation(1) |
|
|
| `backupFileSize()` | 4 | BackupOperation(3), RestoreOperation(1) |
|
|
| `backupPathExists()` | 5 | BackupOperation(2), RestoreOperation(3) |
|
|
| `buildAppDetailsJson()` | 3 | BackupOperation(2), ResticStreamBackup(1) |
|
|
| `listBackupFiles()` | 7 | RestoreScreen(2), RestoreOperation(5) |
|
|
| `mkdirsForBackup()` | 3 | BackupOperation(3) |
|
|
| `backupIsDirectory()` | 1 | (内部使用) |
|
|
|
|
---
|
|
|
|
## 二、Restic 功能调用链
|
|
|
|
### 1. **ResticWrapper 方法调用统计**
|
|
|
|
#### 备份相关
|
|
```
|
|
BackupViewModel.executeBackup()
|
|
├── ResticWrapper.backup() → ResticBackup.backup()
|
|
└── ResticWrapper.backupStreaming() → ResticStreamBackup.backup()
|
|
```
|
|
|
|
#### 恢复相关
|
|
```
|
|
RestoreScreen (UI)
|
|
├── ResticWrapper.restore() → ResticRestore.restore()
|
|
└── ResticWrapper.dump() → ResticRestore.dump()
|
|
```
|
|
|
|
#### 快照管理
|
|
```
|
|
ConfigViewModel
|
|
├── ResticWrapper.listSnapshots() → ResticSnapshotOps.listSnapshots()
|
|
├── ResticWrapper.forget() → ResticSnapshotOps.forget()
|
|
├── ResticWrapper.prune() → ResticMaintenance.prune()
|
|
├── ResticWrapper.unlock() → ResticMaintenance.unlock()
|
|
└── ResticWrapper.stats() → ResticMaintenance.stats()
|
|
|
|
RestoreScreen
|
|
└── ResticWrapper.listSnapshots()
|
|
```
|
|
|
|
#### 初始化
|
|
```
|
|
ConfigViewModel
|
|
└── ResticWrapper.init() → ResticRepoInit.init()
|
|
```
|
|
|
|
### 2. **ResticWrapper 方法详细调用**
|
|
|
|
| 方法 | 调用次数 | 调用者 |
|
|
|------|---------|--------|
|
|
| `backup()` | 2 | BackupViewModel:299, ResticWrapper:175 |
|
|
| `backupStreaming()` | 2 | BackupViewModel:263, ResticWrapper:210 |
|
|
| `restore()` | 2 | RestoreScreen:299, ResticWrapper:245 |
|
|
| `dump()` | 3 | RestoreScreen (多处), ResticWrapper:272 |
|
|
| `listSnapshots()` | 5 | ConfigViewModel(3), RestoreScreen(1), ResticWrapper:296 |
|
|
| `forget()` | 2 | ConfigViewModel:721, ResticWrapper:320 |
|
|
| `prune()` | 2 | ConfigViewModel:750, ResticWrapper:442 |
|
|
| `unlock()` | 3 | ConfigViewModel(2), ResticWrapper:499 |
|
|
| `stats()` | 2 | ConfigViewModel:647, ResticWrapper:480 |
|
|
| `init()` | 3 | ConfigViewModel(2), ResticWrapper:123 |
|
|
| `buildRepoUrl()` | 2 | ConfigViewModel(1), ResticWrapper:512 |
|
|
| `parseAppDetailsJson()` | 4 | RestoreScreen(2), ResticWrapper:401 |
|
|
| `getLatestSnapshotAppDetails()` | 1 | (内部使用) |
|
|
|
|
---
|
|
|
|
## 三、Restic 子模块调用图
|
|
|
|
### 1. **BackendExecutor 调用链**
|
|
```
|
|
ResticBackup.backup()
|
|
└── executor.withBackend()
|
|
|
|
ResticRestore.restore()
|
|
└── executor.withBackend()
|
|
|
|
ResticRestore.dump()
|
|
└── executor.withBackend()
|
|
|
|
ResticSnapshotOps.listSnapshots()
|
|
└── executor.withBackend()
|
|
|
|
ResticSnapshotOps.forget()
|
|
└── executor.withBackend()
|
|
|
|
ResticMaintenance (prune/unlock/check/stats)
|
|
└── executor.runResticWithBackend()
|
|
|
|
ResticStreamBackup.backup()
|
|
├── executor.runResticStreamingWithBackend()
|
|
└── executor.withBackend()
|
|
```
|
|
|
|
### 2. **ResticCommandRunner 调用统计**
|
|
```
|
|
runRestic() - 12 调用
|
|
├── ResticRepoInit.init() (3次: init, snapshots, unlock)
|
|
├── ResticRestore.dump() (1次)
|
|
├── ResticSnapshotOps.listSnapshots() (2次)
|
|
├── ResticSnapshotOps.forget() (1次)
|
|
├── ResticMaintenance (4次: prune/unlock/check/stats)
|
|
└── ResticStreamBackup.backup() (1次: 验证快照)
|
|
|
|
runResticStreaming() - 3 调用
|
|
├── ResticBackup.backup() (1次)
|
|
├── ResticRestore.restore() (1次)
|
|
└── BackendExecutor.runResticStreamingWithBackend() (1次)
|
|
```
|
|
|
|
### 3. **ResticEnvResolver 调用**
|
|
```
|
|
buildLocalEnv() - 被 BackendExecutor.withBackend() 调用 (本地后端)
|
|
buildBridgeEnv() - 被 BackendExecutor.withBackend() 调用 (远程后端)
|
|
```
|
|
|
|
### 4. **RestBridgeRunner 调用**
|
|
```
|
|
withBridge() - 被 BackendExecutor.withBackend() 调用 (远程后端)
|
|
```
|
|
|
|
---
|
|
|
|
## 四、密码管理调用图
|
|
|
|
### PasswordManager 调用统计
|
|
|
|
| 方法 | 调用次数 | 调用者 |
|
|
|------|---------|--------|
|
|
| `init()` | 1 | MainActivity:34 |
|
|
| `getResticPassword()` | 6 | BackupViewModel:258, ConfigViewModel:168,314,354, RestoreScreen:142,294,482,553 |
|
|
| `setResticPassword()` | 3 | ConfigViewModel:172,221 |
|
|
| `getBackendPass()` | 6 | BackupViewModel:259, ConfigViewModel:169,315,358, RestoreScreen:143,297,483,554 |
|
|
| `setBackendPass()` | 3 | ConfigViewModel:175,224 |
|
|
| `isInitialized()` | 0 | (未使用) |
|
|
| `hasResticPassword()` | 0 | (未使用) |
|
|
| `clearAll()` | 0 | (未使用) |
|
|
|
|
---
|
|
|
|
## 五、辅助工具调用图
|
|
|
|
### 1. **BinaryResolver**
|
|
```
|
|
tarPath() - 3 调用
|
|
├── BackupOperation.backupUserData() :350
|
|
└── RestoreOperation (2处) :57,58
|
|
|
|
zstdPath() - 3 调用
|
|
├── BackupOperation.backupUserData() :354
|
|
└── RestoreOperation (2处) :58
|
|
```
|
|
|
|
### 2. **ResticBinary**
|
|
```
|
|
prepare() - 4 调用
|
|
├── MainActivity:30
|
|
├── ConfigViewModel:373
|
|
├── BackupViewModel:254
|
|
└── RestoreScreen:135
|
|
```
|
|
|
|
### 3. **AppScanner**
|
|
```
|
|
getApkPaths() - 3 调用
|
|
├── BackupOperation:158 (备份时)
|
|
├── BackupOperation:660 (构建 JSON 时)
|
|
└── ResticStreamBackup:88
|
|
|
|
hasObbData() - 1 调用
|
|
└── BackupOperation:240
|
|
|
|
hasKeystore() - 1 调用
|
|
└── BackupOperation:176
|
|
|
|
extractIcon() - 1 调用
|
|
└── BackupOperation:265
|
|
```
|
|
|
|
### 4. **WifiManager**
|
|
```
|
|
backup() - 1 调用
|
|
└── BackupViewModel:223
|
|
|
|
restore() - 1 调用
|
|
└── RestoreScreen (UI)
|
|
```
|
|
|
|
---
|
|
|
|
## 六、RootShell 调用分布
|
|
|
|
### 按模块统计
|
|
|
|
| 模块 | RootShell.exec 调用次数 |
|
|
|------|----------------------|
|
|
| BackupOperation | ~55 |
|
|
| RestoreOperation | ~30 |
|
|
| AppScanner | ~10 |
|
|
| ResticStreamBackup | ~10 |
|
|
| WifiManager | ~10 |
|
|
| SELinuxUtil | ~3 |
|
|
| **总计** | **~118** |
|
|
|
|
### 常见操作类型
|
|
|
|
1. **文件操作** (cp, mkdir, rm, chmod, chown) - ~40%
|
|
2. **包管理** (pm install, pm list, pm grant/revoke) - ~20%
|
|
3. **状态检查** (test -d, stat, ls) - ~15%
|
|
4. **系统信息** (dumpsys, pidof, settings) - ~15%
|
|
5. **压缩/归档** (tar, zstd, gzip) - ~10%
|
|
|
|
---
|
|
|
|
## 七、调用关系发现的问题
|
|
|
|
### 1. **未使用的函数**
|
|
```
|
|
PasswordManager.isInitialized() - 0 调用
|
|
PasswordManager.hasResticPassword() - 0 调用
|
|
PasswordManager.clearAll() - 0 调用
|
|
```
|
|
**建议**: 这些函数要么添加调用,要么标记为 public API 供外部使用。
|
|
|
|
### 2. **重复的密码获取逻辑**
|
|
```kotlin
|
|
// BackupViewModel:258-259
|
|
val password = PasswordManager.getResticPassword()
|
|
?: s.config.resticPassword.takeIf { it.isNotEmpty() } ?: ""
|
|
|
|
// ConfigViewModel:168-169
|
|
val password = PasswordManager.getResticPassword()
|
|
?: c.resticPassword.takeIf { it.isNotEmpty() }
|
|
```
|
|
**问题**: 相同的密码获取逻辑在 3+ 处重复。
|
|
**建议**: 提取为公共函数 `getEffectiveResticPassword()`。
|
|
|
|
### 3. **RootShell 调用过多**
|
|
- BackupOperation 中约 55 次 RootShell.exec 调用
|
|
- 很多是简单的文件存在性检查或状态查询
|
|
- 可以优化为批量操作或缓存
|
|
|
|
### 4. **错误处理不一致**
|
|
- 有些 RootShell 调用检查了 `isSuccess`,有些没有
|
|
- 某些调用使用 `?.isSuccess`,某些使用 `.isSuccess`
|
|
- 建议统一错误处理模式
|
|
|
|
### 5. **调用深度过深**
|
|
```
|
|
BackupViewModel.executeBackup()
|
|
→ BackupOperation.backupApps()
|
|
→ backupUserData()
|
|
→ runTar()
|
|
→ RootShell.exec()
|
|
```
|
|
**层数**: 5 层
|
|
**建议**: 考虑扁平化某些调用,减少复杂性。
|
|
|
|
---
|
|
|
|
## 八、性能优化建议
|
|
|
|
### 1. **减少 RootShell 调用**
|
|
```kotlin
|
|
// 当前:多次独立调用
|
|
val exists = RootShell.exec("test -d '$path1'")
|
|
val size = RootShell.exec("stat -c%s '$path2'")
|
|
val perms = RootShell.exec("stat -c%a '$path3'")
|
|
|
|
// 优化:单次批量调用
|
|
val info = RootShell.exec("""
|
|
test -d '$path1' && echo "EXISTS" || echo "NOT_EXISTS"
|
|
stat -c%s '$path2'
|
|
stat -c%a '$path3'
|
|
""")
|
|
```
|
|
|
|
### 2. **缓存应用信息**
|
|
```kotlin
|
|
// 当前:每次都查询
|
|
val version = RootShell.exec("dumpsys package '$pkg' | grep versionCode")
|
|
|
|
// 优化:查询一次,缓存结果
|
|
val versionCache = ConcurrentHashMap<String, String>()
|
|
fun getVersion(pkg: String) = versionCache.getOrPut(pkg) {
|
|
RootShell.exec("dumpsys package '$pkg' | grep versionCode")
|
|
}
|
|
```
|
|
|
|
### 3. **并行执行独立操作**
|
|
```kotlin
|
|
// 当前:串行执行
|
|
backupSsaid(pkg, appDir, userId)
|
|
backupPermissions(pkg, appDir)
|
|
extractIcon(pkg, appDir, userId)
|
|
|
|
// 优化:并行执行(注意线程安全)
|
|
coroutineScope {
|
|
async { backupSsaid(pkg, appDir, userId) }
|
|
async { backupPermissions(pkg, appDir) }
|
|
async { extractIcon(pkg, appDir, userId) }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 九、调用图可视化
|
|
|
|
### 备份流程
|
|
```
|
|
User Click → BackupViewModel.executeBackup()
|
|
├─ BackupOperation.backupApps()
|
|
│ ├─ [并发] backupUserData() → runTar() → RootShell.exec()
|
|
│ ├─ [并发] backupObb() → RootShell.exec()
|
|
│ ├─ [并发] backupExternalData() → RootShell.exec()
|
|
│ ├─ backupSsaid() → RootShell.exec()
|
|
│ ├─ backupPermissions() → RootShell.exec()
|
|
│ ├─ AppScanner.*()
|
|
│ └─ buildAppDetailsJson() → writeFileForBackup()
|
|
│
|
|
├─ WifiManager.backup() → RootShell.exec()
|
|
│
|
|
└─ [可选] ResticWrapper.backup()
|
|
└─ ResticBackup.backup()
|
|
└─ executor.withBackend()
|
|
└─ runner.runResticStreaming()
|
|
└─ ProcessBuilder → restic CLI
|
|
```
|
|
|
|
### 恢复流程
|
|
```
|
|
User Click → RestoreScreen
|
|
├─ ResticWrapper.restore()
|
|
│ └─ ResticRestore.restore()
|
|
│ └─ executor.withBackend()
|
|
│ └─ runner.runResticStreaming()
|
|
│
|
|
└─ RestoreOperation.restoreApps()
|
|
├─ installApk() → RootShell.exec()
|
|
├─ restoreUserData() → RootShell.exec()
|
|
├─ restoreObb() → RootShell.exec()
|
|
├─ restoreExternalData() → RootShell.exec()
|
|
├─ restoreSsaid() → RootShell.exec()
|
|
└─ restorePermissions() → RootShell.exec()
|
|
```
|
|
|
|
---
|
|
|
|
## 十、总结
|
|
|
|
### 调用统计
|
|
- **RootShell.exec**: ~118 次调用
|
|
- **Restic CLI**: ~15 次调用 (通过 ResticCommandRunner)
|
|
- **文件 I/O**: ~30 次调用 (writeFileForBackup, readTextFile)
|
|
- **密码管理**: ~20 次调用 (PasswordManager)
|
|
|
|
### 发现的问题
|
|
1. ✅ **密码获取逻辑重复** - 3+ 处相同代码
|
|
2. ✅ **未使用的函数** - 3 个 PasswordManager 方法
|
|
3. ✅ **RootShell 调用过多** - 可优化为批量操作
|
|
4. ✅ **错误处理不一致** - 需要统一标准
|
|
5. ✅ **调用深度过深** - 5 层嵌套
|
|
|
|
### 优化优先级
|
|
1. **P1**: 提取密码获取公共函数,消除重复
|
|
2. **P2**: 统一错误处理模式
|
|
3. **P3**: 实现 RootShell 批量调用优化
|
|
4. **P4**: 实现应用信息缓存机制
|