Android proguard 混淆代码导致 NullPointerException 确实不应该发生 [英] Android proguard obfuscated code is causing NullPointerException when it really shouldn't be

查看:35
本文介绍了Android proguard 混淆代码导致 NullPointerException 确实不应该发生的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Android Marketplace 上发布了一个应用程序.我从少数用户(可能是 2%)那里收到错误报告,他们收到了没有逻辑意义的 NullPointerExceptions.

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.

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

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.

此时,我唯一能想象的就是与我的 Proguard 混淆有关的事情.我看过其他一些文章,谈到如果您发现异常行为,请取消 -overloadaggressively 选项,但据我所知,我没有使用该选项.

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.

有没有其他人经历过使用 android 和 proguard 的神秘 NPE.人们是否可以推荐其他任何设置来降低可能导致此问题的优化?

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?

还有其他想法吗?

作为参考,这里是获取 NPE 的未混淆函数:

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 是另一个类中的最终变量:

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

game.setScreen(new MainMenuScreen(game));

调度器在上面对 super 的调用中被设置.

dispatcher gets set within in the call to super above.

getSettings() 返回一个在应用程序一开始就设置的设置对象,是私有的,永远不会被取消设置.这个方法之前也用过几次.

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.

没有自动装箱原语.

这里是 proguard 配置:

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.

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

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.

我通过获取 proguard 生成的 obfuscated.jar 并通过反编译器运行它来解决这个问题.这是一个有趣的练习,因为您对 proguard 的内部工作有了更多的了解.对于希望更好地了解 proguard 对其代码的影响的人,我强烈推荐它.

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.

这篇关于Android proguard 混淆代码导致 NullPointerException 确实不应该发生的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆