12 KiB
12 KiB
函数调用完整分析报告
基于对代码的全面搜索,以下是各核心函数的调用情况分析。
一、备份功能调用链
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 |
常见操作类型
- 文件操作 (cp, mkdir, rm, chmod, chown) - ~40%
- 包管理 (pm install, pm list, pm grant/revoke) - ~20%
- 状态检查 (test -d, stat, ls) - ~15%
- 系统信息 (dumpsys, pidof, settings) - ~15%
- 压缩/归档 (tar, zstd, gzip) - ~10%
七、调用关系发现的问题
1. 未使用的函数
PasswordManager.isInitialized() - 0 调用
PasswordManager.hasResticPassword() - 0 调用
PasswordManager.clearAll() - 0 调用
建议: 这些函数要么添加调用,要么标记为 public API 供外部使用。
2. 重复的密码获取逻辑
// 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 调用
// 当前:多次独立调用
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. 缓存应用信息
// 当前:每次都查询
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. 并行执行独立操作
// 当前:串行执行
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)
发现的问题
- ✅ 密码获取逻辑重复 - 3+ 处相同代码
- ✅ 未使用的函数 - 3 个 PasswordManager 方法
- ✅ RootShell 调用过多 - 可优化为批量操作
- ✅ 错误处理不一致 - 需要统一标准
- ✅ 调用深度过深 - 5 层嵌套
优化优先级
- P1: 提取密码获取公共函数,消除重复
- P2: 统一错误处理模式
- P3: 实现 RootShell 批量调用优化
- P4: 实现应用信息缓存机制