混淆后如何生成 mapping.txt 文件用于崩溃堆栈还原?

解读

国内面试里,这道题表面问“怎么拿到 mapping.txt”,实则考察候选人是否真正走过“发版→崩溃→还原”完整闭环。很多候选人只答“打开 minifyEnabled 就有”,却说不清:

  1. 构建链路中谁负责写 mapping.txt;
  2. 国内多渠道(华为、OPPO、小米、应用宝)各自加固再混淆时 mapping.txt 是否会被覆盖;
  3. 线上 Native 崩溃、Java 崩溃、Flutter 崩溃三种堆栈分别怎么用 mapping.txt 还原;
  4. 合规场景下 mapping.txt 含类名方法名,属于“可逆敏感信息”,如何存储与授权访问。

能把这四层讲清楚,才能体现工程化与安全意识。

知识点

  1. R8 / ProGuard 映射文件生成阶段:AGP 7.0+ 默认 R8,在 variant/minified Task 里通过 -printmapping build/outputs/mapping/variant/mapping.txt 输出。
  2. 多渠道包场景:加固厂商(腾讯乐固、阿里聚安全)会二次混淆,必须在其控制台上传原始 mapping.txt,否则还原结果错乱。
  3. 还原工具链:
    • Java/Kotlin:Google 自带 retrace.jar / AS 内置“Analyze Stack Trace”;
    • Native:需配合 ndk-stackobjdump,不受 mapping.txt 管辖;
    • Flutter:Dart AOT 使用 flutter symbolize,需 app.android-arm64.symbols 文件,与 mapping.txt 无关。
  4. 合规与 CI:mapping.txt 需进入受控仓库(Git LFS 或私有 Nexus),权限粒度到“仅崩溃平台可读取”,避免直接随 apk 分发。
  5. 自动化:在 CI 最后一步把 mapping.txt 作为产物上传到自研或第三方崩溃平台(如 Bugly、Firebase、Sentry),并打上与 apk 相同的 versionCode+git-sha 标签,保证一对一匹配。

答案

  1. 在模块级 build.gradle 开启混淆:
android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            // 显式指定输出路径,防止被多渠道任务覆盖
            mappingFileUploadEnabled true
        }
    }
}

R8 会在 app/build/outputs/mapping/release/mapping.txt 自动生成,无需手动写规则。

  1. CI 中固化产物:
./gradlew assembleRelease
# 拷贝并重命名,带上 git commit id
cp app/build/outputs/mapping/release/mapping.txt \
   mapping-${VERSION_CODE}-${GIT_SHA}.txt
# 上传至内部 Maven 或崩溃平台
curl -F "file=@mapping-${VERSION_CODE}-${GIT_SHA}.txt" \
     https://crash.mycompany.com/api/uploadMapping
  1. 线上收到堆栈后,用对应版本的 mapping.txt 本地还原:
retrace.sh -verbose mapping.txt crash.txt > readable.txt

或在 Android Studio 直接粘贴堆栈,选择“Add ProGuard Mapping”即可。

  1. 若使用加固,务必在加固后台再传一次原始 mapping.txt,并勾选“保留原混淆规则”,否则行号会偏移。

  2. 合规:mapping.txt 不随 apk 发布,不提交到公开仓库;访问需 OA 审批,存储加密 at rest,90 天后自动归档。

拓展思考

  1. 大型 App 采用“组件化 + 多 AAR” 时,每个 AAR 可能独立开启混淆,最终会出现多个 mapping.txt。此时需在 CI 里把所有 AAR 的 mapping 文件按模块名合并成 mapping-full.txt,再上传崩溃平台;否则堆栈里出现 com.xxx.modulea.a.b 时无法还原。
  2. 为了加快 CI,可在 gradle.properties 里加 android.enableR8.fullMode=false,牺牲少量优化换取 30% 构建时间,但 mapping.txt 体积会增大,需要评估崩溃还原平台的单文件大小限制。
  3. 未来 Google 强制 AAB 上架,Play 控制台自带“Deobfuscation files” 页签,国内厂商商店也在跟进。届时 mapping.txt 将像符号表一样成为强制上传项,团队需要提前把“上传-匹配-下载”做成 CLI 脚本,避免发版当天人工拖文件。
  4. 若项目混编 Kotlin 与 Java,Kotlin 的 inline 函数、lambda 名在 mapping.txt 里会出现 access$lambda-0 这类符号,还原后仍不易读;可额外在 proguard-rules.pro 中加 -keepattributes kotlin.Metadata 并上传 kotlin-module 元数据,配合 Sentry 的 Kotlin 插件,能把 lambda 还原成可读的 doLogin$lambda-0$impl 形式,进一步缩短排障时间。