R8混淆后使用retrace还原正确行号的方法 及release/debug构建行号异常排查
我当前使用的开发环境为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

