Files
android-backup-gui/function-call-analysis.md

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**: 实现应用信息缓存机制