Запутанный код Android Proguard вызывает исключение NullPointerException, когда это действительно не должно быть - PullRequest
6 голосов
/ 02 октября 2011

Я распространил приложение на Android Marketplace.Я получаю отчеты об ошибках от небольшой группы пользователей (возможно, 2%), где они получают исключения NullPointerException, где это не имеет логического смысла.

Я никогда не мог повторить это сам.Код относительно прост и представляет собой общий путь кода, которым должен следовать КАЖДЫЙ пользователь.Я на самом деле взял каждую отдельную строку кода, которая могла бы создать NPE, и обернул его в блок try-catch и выдал пользовательское исключение времени выполнения, но я все еще получаю ошибки NullPointerException, которые не были обнаружены.

На данный момент, единственное, что я могу себе представить, это что-то, связанное с моей обфускацией Proguard.Я видел другую статью, в которой говорилось о том, чтобы убрать опцию -overloadaggressively, если вы заметили странное поведение, но, насколько я могу судить, я не использую эту опцию.

Кто-нибудь еще сталкивался с таинственными NPE, использующими Androidи прогард.Есть ли другие параметры, которые люди могут порекомендовать набрать оптимизацию, которая может быть причиной этой проблемы?

Любые другие идеи?

Для справки, вот необъясненная функция, которая получает 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.setScreen(new MainMenuScreen(game));

Диспетчер устанавливается в вызове 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 *;
}

Ответы [ 3 ]

8 голосов
/ 04 октября 2011

ОК, я думаю, что я дошел до корня проблемы / путаницы.

Одна из вещей, которую делает proguard, - это встроенные некоторые методы.Из-за этого все содержимое моей функции setupContinue () в нижней части моего конструктора было добавлено непосредственно в содержимое моего конструктора.Так что теперь у меня есть еще куча кода для обзора, и я вижу еще больше возможностей для NPE.Я почти уверен, что докопаюсь до сути вопроса.

Я понял это, взяв obfuscated.jar, который создает proguard, и запустив его через декомпилятор.Это интересное упражнение, поскольку вы получаете немного больше понимания внутренней работы Proguard.Я очень рекомендую это для людей, желающих лучше понять влияние, которое proguard оказывает на их код.

2 голосов
/ 02 октября 2011

Лучше всего было бы использовать файл mapping.txt и инструмент восстановления, чтобы найти точное место ошибки.Оттуда будет легче понять, действительно ли это Proguard или какой-то другой конечный случай, о котором вы не задумывались.

Для этого вам нужно скопировать трассировку стека из консоли разработчика в другой файл,давайте предположим, что он называется

c: \ trace.txt

Теперь в вашем проекте вы найдете папку Proguard с 4 файлами.Давайте предположим, что ваш проект находится в

c: \ project

Что вам нужно сделать, это запустить инструмент восстановления (используя пакетный файл для более легкого использования), расположенныйat (изменить местоположение папки Android Sdk):

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

Goв эту папку и запустите:

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

С этого момента это будет намного прощечтобы понять точную линию исключения и, вероятно, найти ошибку.

Из моего опыта, единственное, что может быть испорчено, это сторонние библиотеки.Обычный код Android для всех моих проектов никогда не пострадал от запутывания.

1 голос
/ 03 октября 2011

Извините, я не могу оставлять комментарии (я новичок в SO).

Это может быть еще одна проблема, с которой я столкнулся. На некоторых телефонах в какой-то момент возникла проблема с отсутствующими библиотеками Android, такими как библиотека JSon.

Я бы порекомендовал вам поближе взглянуть на то, какие телефоны действительно получают NPE - там могут быть некоторые сходства.

В моем случае это был HTC Desire Z, в котором отсутствовала библиотека JSon, и поэтому приложение закрывалось при каждом вызове части JSon. Позже HTC исправила эту проблему с исправлением в роме Desire Z.

...