Android proguard混淆代码导致NullPointerException,当它真的不应该

我在Android Marketplace上发布了一个应用程序。 我从一小部分用户(可能是2%)收到错误报告,他们在那里获得NullPointerExceptions,但这在逻辑上没有意义。

我自己从来没有能够复制过这个。 代码相对简单,是每个用户必须遵循的通用代码路径。 我实际上已经采用了可能创建NPE并将其包装在try-catch块中并抛出自定义运行时exception的每个单独的代码行,但我仍然得到未捕获的NullPointerException错误。

在这一点上,我唯一能想到的就是与我的Proguard混淆相关的东西。 我已经看到其他一些文章谈论如果你注意到奇怪的行为,取出-overloadaggressively选项,但据我所知,我没有使用该选项。

有没有其他人使用androidproguard经历过神秘的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(); } 

所以我能看到的唯一可能是游戏,调度员和设置。

游戏通过此代码在另一个类中设置。 游戏是其他类中的最终variables:

 game.setScreen(new MainMenuScreen(game)); 

调度员在超级电话的调用中设置。

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 (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 (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 ; } -keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * { public (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 *; } 

好吧,我认为我find了问题/困惑的根源。

proguard所做的一件事就是内联一些方法。 因此,我的构造函数底部的setupContinue()函数的全部内容被直接添加到我的构造函数的内容中。 所以现在我有更多的代码要审查,我确实看到了NPE的更多可能性。 我很确定我会解决问题的根源。

我通过获取proguard生成的obfuscated.jar并通过反编译器运行它来解决这个问题。 这是一个有趣的练习,因为你可以更深入地了解proguard的内部工作原理。 我强烈建议那些想要更好地理解proguard对其代码的影响的人。

您最好的选择是使用mapping.txt文件和回溯工具来查找错误的确切位置。 从那里可以更容易理解它是否确实是Proguard或其他一些您没有想到的最终案例。

为此,您需要将堆栈跟踪从开发人员的控制台复制到另一个文件,让我们假设它被调用

C:\ trace.txt

现在,在您的项目中,您将find包含4个文件的Proguard文件夹。 我们假设你的项目在

C:\项目

您需要做的是运行位于(更改为Android Sdk文件夹位置)的回溯工具(使用批处理文件以便于使用):

C:\ Android的SDK-WINDOWS \工具\ proguard的\ BIN \ retrace.bat

转到该文件夹​​并运行:

retrce c:\ project \ proguard \ mapping.txt c:\ trace.txt

从那以后,可以更容易地确定exception的确切行并可能find错误。

根据我的经验,唯一可能搞得搞糟的是第三方图书馆。 对于我的所有项目,普通的Android代码从未因混淆而受到损害。

对不起,我还不能发表评论(我是SO的新手)。

这可能是我自己遇到的另一个问题。 在某些手机上,在某些方面存在一个问题,即缺少像JSon库这样的Android库。

我建议你仔细看看实际获得NPE的手机 – 可能有一些相似之处。

在我的情况下,它是HTC Desire Z,它缺少了JSon库,因此每次调用JSon部件时app力都会关闭。 HTC稍后使用针对Desire Z的rom的修补程序修复了这个问题。