You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R8混淆后使用retrace还原正确行号的方法 及release/debug构建行号异常排查

Android R8混淆后栈迹行号相关问题

我当前使用的开发环境为Android Studio 4.1.3及其配套内置工具链(AGP 4.1.3、Gradle 6.5、Android SDK Build Tools 31-rc2、Platform Tools 31.0.1、SDK Tools 26.1.1),代码混淆采用默认的R8工具。

我当前对release构建的混淆配置如下:

release {
   // 仅针对release构建启用代码压缩、混淆和优化
   minifyEnabled true

   // 由Android Gradle插件执行的资源压缩功能
   shrinkResources false

   // 包含Android Gradle插件自带的默认ProGuard规则文件
   proguardFiles getDefaultProguardFile(
           'proguard-android.txt'),
           'proguard-rules.pro'
}

我的proguard-rules.pro文件顶部的配置内容如下:

# 隐藏原始源文件名
-renamesourcefileattribute SourceFile

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

release构建产物生成后,我拿到的异常栈迹示例如下:首行可见类名已被混淆为SplashActivity$a,源文件名被重命名为“SourceFile”,行号被混淆为2:

at com.reddragon.intouch.ui.SplashActivity$a.onStart(SourceFile:2)
at io.reactivex.rxjava3.observers.DisposableObserver.onSubscribe(SourceFile:2)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.onSubscribe(SourceFile:15)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.subscribeActual(SourceFile:2)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:12)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.subscribeActual(SourceFile:4)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:12)
at io.reactivex.rxjava3.core.Observable.subscribeWith(SourceFile:1)
at com.reddragon.intouch.ui.SplashActivity.onCreate(SourceFile:16)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

我使用位于~\SDK\tools\proguard\bin路径下的retrace.bat工具,搭配本次release构建生成的对应mappings.txt文件对上述栈迹进行反混淆处理,得到的输出结果如下:

at com.reddragon.intouch.ui.SplashActivity$1.void onStart()(SourceFile:2)
at io.reactivex.rxjava3.observers.DisposableObserver.void onSubscribe(io.reactivex.rxjava3.disposables.Disposable)(SourceFile:2)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.void onSubscribe(io.reactivex.rxjava3.disposables.Disposable)(SourceFile:15)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.void subscribeActual(io.reactivex.rxjava3.core.Observer)(SourceFile:2)
at io.reactivex.rxjava3.core.Observable.void subscribe(io.reactivex.rxjava3.core.Observer)(SourceFile:12)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.void subscribeActual(io.reactivex.rxjava3.core.Observer)(SourceFile:4)
at io.reactivex.rxjava3.core.Observable.void subscribe(io.reactivex.rxjava3.core.Observer)(SourceFile:12)
at io.reactivex.rxjava3.core.Observable.io.reactivex.rxjava3.core.Observer subscribeWith(io.reactivex.rxjava3.core.Observer)(SourceFile:1)
at com.reddragon.intouch.ui.SplashActivity.void onCreate(android.os.Bundle)(SourceFile:16)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

可以看到类名已被正确反混淆(SplashActivity$a还原为SplashActivity$1),但行号并未被还原,仍显示错误的数值2。为排查问题,我在build.gradle中为debug构建添加了相同的混淆配置,内容如下:

debug {
  debuggable true
  minifyEnabled true

  // 由Android Gradle插件执行的资源压缩功能
  shrinkResources false

  // 包含Android Gradle插件自带的默认ProGuard规则文件
  proguardFiles getDefaultProguardFile(
          'proguard-android.txt'),
          'proguard-rules.pro'
}

针对开启了上述配置的debug构建,我获取到的栈迹显示:源文件名同样被隐藏为“SourceFile”,类名已被混淆,但行号未做混淆处理,展示为原始真实行号,对应栈迹如下:

at com.reddragon.intouch.ui.SplashActivity$a.onStart(SourceFile:313)
at io.reactivex.rxjava3.observers.DisposableObserver.onSubscribe(SourceFile:74)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.onSubscribe(SourceFile:106)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.subscribeActual(SourceFile:34)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:13095)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.subscribeActual(SourceFile:45)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:13095)
at io.reactivex.rxjava3.core.Observable.subscribeWith(SourceFile:13148)
at com.reddragon.intouch.ui.SplashActivity.onCreate(SourceFile:309)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

目前存在两个核心问题需要排查:

  • 为何release构建生成的栈迹经过retrace处理后无法还原得到原始正确行号?
  • 为何开启了混淆配置的debug构建中行号未被进行混淆处理?

问题原因与解决方案

1. debug构建行号未混淆的原因

你给debug配置了debuggable true,R8默认会对debuggable的构建关闭行号混淆优化。这是Android构建系统的默认行为:调试构建需要保留真实行号方便断点调试,即使开启了minifyEnabled,只要标记为debuggable,R8就不会压缩、混淆行号信息。

如果需要验证这个逻辑,可以临时把debug配置里的debuggable改为false,重新构建后获取栈迹,就能看到行号被混淆的效果。

2. release构建retrace无法还原行号的原因

你当前使用的是旧版SDK里的~\SDK\tools\proguard\bin下的retrace,属于ProGuard的工具,和AGP 4.1.3配套的R8生成的mappings格式存在兼容问题:

  • 你虽然保留了LineNumberTable属性,但release构建时R8默认会对代码做优化处理,合并、删除、重排原始指令块,为了栈迹紧凑性会把原始行号映射为连续的小行号(就是你看到的2、15这类数值)。
  • R8生成的mappings.txt里的行号映射格式和传统ProGuard不一致,旧版retrace无法解析这部分内容,因此只能还原类名、方法名,无法还原行号。

解决方法:

  • 不要使用旧SDK里的ProGuard retrace,改用AGP内置的R8 retrace工具,路径为Android Studio安装目录\plugins\android\lib\retrace\bin\retrace.bat,也可以直接用Android Studio的ProGuard工具面板自动反混淆。
  • 如果需要命令行操作,可以配置Gradle的retrace任务,直接调用AGP内置的retrace,避免兼容问题。
  • 如果不需要R8的优化处理,可以在proguard-rules.pro里添加-dontoptimize,这样R8不会重排指令块,旧版retrace也能正确解析行号映射,但会损失代码优化带来的体积、性能收益。

内容的提问来源于stack exchange,提问作者tfrysinger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 07:45:10