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

Pau*_*aul 6 java android proguard nullpointerexception

我已在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();


}
Run Code Online (Sandbox Code Playgroud)

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

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

game.setScreen(new MainMenuScreen(game));
Run Code Online (Sandbox Code Playgroud)

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

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 *;
}
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 8

好吧,我认为我找到了问题的根源/困惑.

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

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


Inc*_*App 2

最好的选择是使用mapping.txt 文件和retrace 工具来查找错误的确切位置。从那里可以更容易地理解它是否确实是 Proguard 或者其他你没有想到的最终情况。

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

c:\trace.txt

现在,在您的项目中,您将找到一个包含 4 个文件的 Proguard 文件夹。假设您的项目位于

c:\项目

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

c:\android-sdk-windows\tools\proguard\bin\retrace.bat

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

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

从那时起,找出异常的确切行并可能找到错误就会容易得多。

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