Android的ProGuard的混淆code导致NullPointerException异常时,它确实不应该异常、确实、ProGuard、Android

2023-09-05 07:12:12 作者:嘲笑你的冷漠°

我已经分布在Android市场的应用程序。我从一个小的少数用户(也许2%),他们在那里得到NullPointerExceptions它没有任何逻辑意义收到错误报告。最

我从来没有能够复制这个自己。在code是相对比较简单,是每一个用户必须遵循一个共同的code路径。其实我已经采取了一切独立的可能可以创造NPE和包裹在一个try-catch块,并抛出一个自定义的运行时异常code线,但我仍然得到没有抓到NullPointerException异常错误。

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

具有使用Android和ProGuard的其他人有经验的神秘的NPE。是否有任何其他设置的人可以推荐给拨下来,可能会造成这个问题的优化?

任何其他的想法?

有关引用,这里是是非模糊的功能是获得NPE:

 公共MainMenuScreen(最终HauntedCarnival游戏){
    超(游戏);

    game.startMusic(数据/音乐/ intro.mp3);

    舞台=新阶段(Screen.SCREEN_WIDTH,Screen.SCREEN_HEIGHT,真正的);
    stage.addActor(新形象(背景,Assets.mainMenuBackground));
    图像标题=新形象(标题,Assets.mainMenuTitle);
    title.x = 0;
    title.y = 340;
    resetEyeBlink();
    stage.addActor(职称);
    dispatcher.registerInputProcessor(阶段);
    设置= game.getSettings();

    eyeBlinkImage =新形象(眨眼,Assets.eyeBlink);
    如果(settings.getPlayers()。的isEmpty()){
        settings.addPlayer(玩家一);
        settings.save(游戏);
    }
    setupContinue();


}
 

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

游戏中得到通过这个code另一个类设置。比赛是在其他类中的最后一个变量:

  game.setScreen(新MainMenuScreen(游戏));
 
android混淆保留内部类,混淆jar包总结

Dispatcher获取在通话设置中的超级上面。

的getSettings()返回被设置在应用程序的最开始就设定目标,是私有的,永远不会取消设置。它也采用此方法前几次。

有没有自动装箱元。

下面是ProGuard的配置:

  -optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dont preverify
-verbose
-optimizations!code /简/算术,!现场/ * ,!类/合并/ *

-keepattributes签名

-keep公共类com.alkilabs.hauntedcarnival.settings.Settings
-keep公共类com.alkilabs.hauntedcarnival.settings.Settings {
    *;
}
-keep公共类com.alkilabs.hauntedcarnival.settings.Player
-keep公共类com.alkilabs.hauntedcarnival.settings.Player {
    *;
}
-keepnames公共类com.alkilabs.hauntedcarnival.world.World
-keepnames公共类*扩展com.alkilabs.hauntedcarnival.world.upgrades.Upgrade
-keepnames公共类*扩展com.alkilabs.hauntedcarnival.world.achievments.Achievement

-keepnames公共类com.alkilabs.hauntedcarnival.world.monsters.MonsterType
-keepclassmembers类*扩展com.alkilabs.hauntedcarnival.world.monsters.Monster {
    公共和LT; INIT>(com.alkilabs.hauntedcarnival.world.monsters.MonsterType,为java.lang.Integer,com.alkilabs.hauntedcarnival.world.World);
}

-keepnames公共类com.alkilabs.hauntedcarnival.world.items.ItemType
-keepclassmembers类*扩展com.alkilabs.hauntedcarnival.world.items.Item {
    公共和LT; INIT>(com.alkilabs.hauntedcarnival.world.World,为java.lang.Integer,为java.lang.Integer);
}


-keep公共类*扩展android.app.Activity
-keep公共类*扩展android.app.Application
-keep公共类*扩展android.app.Service
-keep公共类*扩展android.content.BroadcastReceiver
-keep公共类*扩展android.content.ContentProvider
-keep公共类*扩展android.app.backup.BackupAgentHelper
-keep公共类*扩展的Andr​​oid。preference。preference

-dontwarn com.badlogic.gdx.scenes.scene2d.ui.utils.DesktopClipboard
-dontwarn com.badlogic.gdx.utils.JsonWriter
-dontwarn com.badlogic.gdx.utils.XmlWriter

-keepclasseswithmembernames类* {
    天然的LT;方法&gt ;;
}

-keepclasseswithmembers类* {
    公共和LT; INIT>(android.content.Context,android.util.AttributeSet);
}

-keepclasseswithmembers类* {
    公共和LT; INIT>(android.content.Context,android.util.AttributeSet,INT);
}

-keepclassmembers类*扩展android.app.Activity {
   公共无效*(android.view.View);
}

-keepclassmembers枚举* {
    公共静态** []值();
    公共静态**的valueOf(java.lang.String中);
}

-keep类*实现android.os.Parcelable {
  公共静态最终android.os.Parcelable $造物主*;
}
 

解决方案

OK,我想我的问题/混乱的根源。

的事情之一ProGuard的作用是在网上的一些方法。正因为如此,我的setupContinue()函数中的我的构造底部的整个内容物直接添加到我的构造的内容。所以,现在我有一大堆更多的code审查,我的确看到了NPE的一些更多的可能性。我是pretty的肯定,我会得到我的问题的底部。

我想通了这一点,采取了obfuscated.jar的ProGuard的产生,并通过反编译运行它。它的一个有趣的练习当你得到一点更深入地了解ProGuard的内部运作。我强烈推荐它的人想更好地了解影响了ProGuard的对他们的code。

I have distributed an application on the Android Marketplace. I am getting error reports back in from a small handful of users (maybe 2%) where they are getting NullPointerExceptions where it doesn't make logical sense.

I have never been able to replicate this myself. The code is relatively straightforward and is a common code path that EVERY user has to follow. I've actually taken every separate line of code that could possibly be creating the NPE and wrapped it in a try-catch block and throw a custom runtime exception, but I'm still getting NullPointerException errors not caught.

At this point, the only thing I can imagine it would be is something related to my Proguard obfuscation. I have seen some other article talking about taking out the -overloadaggressively option if you notice odd behavior, but as far as I can tell, I'm not using that option.

Has anybody else experienced mysterious NPEs using android and proguard. Are there any other settings people can recommend to dial down the optimizations that might be causing this issue?

Any other ideas?

For reference, here is the unobfuscated function that is getting the 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();


}

So the only possibilities I can see are game, dispatcher and settings.

game gets set via this code in another class. game is a final variable in that other class:

game.setScreen(new MainMenuScreen(game));

dispatcher gets set within in the call to super above.

getSettings() returns a settings object that gets set at the very start of the application, is private and never gets unset. Its also used before this method several times.

There are not auto-boxing primitives.

here is the proguard config:

-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 *;
}

解决方案

OK, I think I got to the root of the issue/confusion.

One of the things proguard does is in-line some methods. Because of this, the entire contents of my setupContinue() function at the bottom of my constructor was added directly into the contents of my constructor. So now I have a bunch more code to review, and I do see some more possibilities for NPEs. I'm pretty sure I'll get to the bottom of my issue.

I figured this out by taking the obfuscated.jar that proguard produces, and running it through a decompiler. Its an interesting exercise as you get get little more insight into the inner workings of proguard. I highly recommend it for people wanting to better understand the impacts that proguard has on their code.