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

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

常见操作类型

  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. 重复的密码获取逻辑

// 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)

发现的问题

  1. 密码获取逻辑重复 - 3+ 处相同代码
  2. 未使用的函数 - 3 个 PasswordManager 方法
  3. RootShell 调用过多 - 可优化为批量操作
  4. 错误处理不一致 - 需要统一标准
  5. 调用深度过深 - 5 层嵌套

优化优先级

  1. P1: 提取密码获取公共函数,消除重复
  2. P2: 统一错误处理模式
  3. P3: 实现 RootShell 批量调用优化
  4. P4: 实现应用信息缓存机制