Android proguard 混淆代码实际上不应该导致 NullPointerException
Posted
技术标签:
【中文标题】Android proguard 混淆代码实际上不应该导致 NullPointerException【英文标题】:Android proguard obfuscated code is causing NullPointerException when it really shouldn't be 【发布时间】:2011-11-29 12:41:02 【问题描述】:我在 android Marketplace 上分发了一个应用程序。我收到了一小部分用户(可能是 2%)的错误报告,他们收到了 NullPointerExceptions,而这在逻辑上没有意义。
我自己从来没有复制过这个。该代码相对简单,是每个用户都必须遵循的通用代码路径。实际上,我已经获取了可能创建 NPE 的每一行单独的代码,并将其包装在一个 try-catch 块中并抛出一个自定义运行时异常,但我仍然没有捕获到 NullPointerException 错误。
在这一点上,我唯一能想象的就是与我的 Proguard 混淆有关的事情。如果您发现奇怪的行为,我已经看到其他一些文章谈到取出 -overloadaggressively 选项,但据我所知,我没有使用该选项。
有没有其他人使用 android 和 proguard 体验过神秘的 NPE。是否有任何其他设置可供人们推荐以降低可能导致此问题的优化?
还有其他想法吗?
作为参考,这里是获取 NPE 的未混淆函数:
public MainMenuScreen(final HauntedCarnival game)
super(game);
game.startMusic("data/music/intro.mp3");
stage = new Stage(Screen.SCREEN_WIDTH, Screen.SCREEN_HEIGHT,true);
stage.addActor(new Image("background", Assets.mainMenuBackground));
Image title = new Image("title", Assets.mainMenuTitle);
title.x = 0;
title.y = 340;
resetEyeBlink();
stage.addActor(title);
dispatcher.registerInputProcessor(stage);
settings = game.getSettings();
eyeBlinkImage = new Image("eyeBlink", Assets.eyeBlink);
if (settings.getPlayers().isEmpty())
settings.addPlayer("Player One");
settings.save(game);
setupContinue();
所以我能看到的唯一可能性是游戏、调度程序和设置。
通过此代码在另一个类中设置游戏。 game 是另一个类中的最终变量:
game.setScreen(new MainMenuScreen(game));
dispatcher 在上面对 super 的调用中被设置。
getSettings() 返回一个设置对象,该对象在应用程序的一开始就设置,是私有的并且永远不会取消设置。在此方法之前也使用过几次。
没有自动装箱原语。
这里是 proguard 配置:
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keepattributes Signature
-keep public class com.alkilabs.hauntedcarnival.settings.Settings
-keep public class com.alkilabs.hauntedcarnival.settings.Settings
*;
-keep public class com.alkilabs.hauntedcarnival.settings.Player
-keep public class com.alkilabs.hauntedcarnival.settings.Player
*;
-keepnames public class com.alkilabs.hauntedcarnival.world.World
-keepnames public class * extends com.alkilabs.hauntedcarnival.world.upgrades.Upgrade
-keepnames public class * extends com.alkilabs.hauntedcarnival.world.achievments.Achievement
-keepnames public class com.alkilabs.hauntedcarnival.world.monsters.MonsterType
-keepclassmembers class * extends com.alkilabs.hauntedcarnival.world.monsters.Monster
public <init>(com.alkilabs.hauntedcarnival.world.monsters.MonsterType, java.lang.Integer, com.alkilabs.hauntedcarnival.world.World);
-keepnames public class com.alkilabs.hauntedcarnival.world.items.ItemType
-keepclassmembers class * extends com.alkilabs.hauntedcarnival.world.items.Item
public <init>(com.alkilabs.hauntedcarnival.world.World, java.lang.Integer, java.lang.Integer);
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-dontwarn com.badlogic.gdx.scenes.scene2d.ui.utils.DesktopClipboard
-dontwarn com.badlogic.gdx.utils.JsonWriter
-dontwarn com.badlogic.gdx.utils.XmlWriter
-keepclasseswithmembernames class *
native <methods>;
-keepclasseswithmembers class *
public <init>(android.content.Context, android.util.AttributeSet);
-keepclasseswithmembers class *
public <init>(android.content.Context, android.util.AttributeSet, int);
-keepclassmembers class * extends android.app.Activity
public void *(android.view.View);
-keepclassmembers enum *
public static **[] values();
public static ** valueOf(java.lang.String);
-keep class * implements android.os.Parcelable
public static final android.os.Parcelable$Creator *;
【问题讨论】:
您能否发布导致您使用的 NPE 和 ProGuard 设置的代码? 我添加了有关特定代码和我的 proguard 配置的更多详细信息 【参考方案1】:好的,我想我找到了问题/困惑的根源。
proguard 所做的其中一件事是内联一些方法。因此,我的构造函数底部的 setupContinue() 函数的全部内容被直接添加到我的构造函数的内容中。所以现在我有更多代码要查看,而且我确实看到了 NPE 的更多可能性。我很确定我会深入了解我的问题。
我通过使用 proguard 生成的 obfuscated.jar 并通过反编译器运行它来解决这个问题。这是一个有趣的练习,因为您对 proguard 的内部工作有了更多的了解。对于希望更好地了解 proguard 对其代码的影响的人们,我强烈推荐它。
【讨论】:
查看已处理的代码可能非常有用。您应该确保您使用的是最新版本的 ProGuard(目前为 4.6 或 4.7 beta)。值得注意的是,4.6 版包含一个与静态初始化程序相关的修复,该修复程序由于方法被移动(内联)而无法运行。不过,令人惊讶的是,问题并没有持续发生。 Proguard 内联代码的部分帮助很大。现在我意识到为什么我的用户提交的崩溃/堆栈跟踪似乎以方法签名而不是导致 NPE 的实际代码行结束。太糟糕了,它没有专门指向导致问题的行。如果该方法包含大量代码(就像大海捞针一样),那真的很糟糕。【参考方案2】:最好的办法是使用 mapping.txt 文件和回溯工具来查找错误的确切位置。 从那里可以更容易理解它是否确实是 Proguard 或您没有想到的其他最终情况。
为此,您需要将堆栈跟踪从开发人员控制台复制到另一个文件,假设它被称为
c:\trace.txt
现在,在您的项目中,您会找到一个包含 4 个文件的 Proguard 文件夹。 假设您的项目在
c:\项目
您需要做的是运行位于(更改为您的 Android Sdk 文件夹的位置)的回溯工具(使用批处理文件以便于使用):
c:\android-sdk-windows\tools\proguard\bin\retrace.bat
转到该文件夹并运行:
回溯 c:\project\proguard\mapping.txt c:\trace.txt
从那里开始,找出异常的确切行并可能找到错误会容易得多。
根据我的经验,唯一可能会搞砸的是第三方库。对于我所有的项目,普通的 Android 代码从未受到混淆的伤害。
【讨论】:
是的,我已经这样做了。我知道确切的方法和调用该方法的整个堆栈跟踪。我唯一不知道的是行号,但这是因为这是一个没有行号的生产版本。另外,据我了解,Proguard 实际上会在优化阶段重写您的一些代码(这是我怀疑 proguard 的原因之一)。所以我被降级为每条可以产生 NPE 的线的老式调试,但可惜,没有骰子。 所以我对你的唯一建议是将一个未混淆的版本放置几天,看看你是否收到额外的错误报告......我从未在我的代码中遇到过 Proguard 错误(来自最复杂的应用程序到最简单的应用程序),我总是混淆我的项目。 是的,我担心它可能会出现这种情况,但不幸的是,我只是不打算让我的代码公之于众。我看到其他一些帖子谈论手机上的proguard代码问题:***.com/questions/93290/…所以我认为这不是前所未有的。我只是希望其他人可能有类似的情况。 这是从 2008 年开始的。现在 Proguard 是具有预定义设置的 Android Sdk 的一部分。 @IncrediApp 我在 2020 年也有同样的问题【参考方案3】:抱歉,我还不能发布 cmets(我是 SO 新手)。
这可能是我自己遇到的另一个问题。在某些手机上,有时会出现缺少 Android 库(如 JSon 库)的问题。
我建议您仔细看看哪些手机实际上获得了 NPE - 可能有一些相似之处。
在我的情况下,它是 HTC Desire Z,它缺少 JSon 库,因此每次调用 JSon 部分时应用程序强制关闭。 HTC 稍后通过对 Desire Z 的 rom 的修补程序修复了该问题。
【讨论】:
以上是关于Android proguard 混淆代码实际上不应该导致 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章