2011-10-02 19 views
6

He distribuido una aplicación en Android Marketplace. Recibo informes de error de un pequeño grupo de usuarios (quizás un 2%) donde obtienen NullPointerExceptions donde no tiene sentido lógico.El código ofuscado de Android Proguard causa NullPointerException cuando realmente no debería ser

Nunca he podido replicar esto yo mismo. El código es relativamente sencillo y es una ruta de código común que CADA usuario debe seguir. De hecho, tomé cada línea de código por separado que podría estar creando el NPE y lo envolví en un bloque try-catch y lancé una excepción de tiempo de ejecución personalizada, pero todavía estoy obteniendo errores de NullPointerException no detectados.

En este punto, lo único que puedo imaginar es algo relacionado con mi ofuscación de Proguard. He visto algún otro artículo hablando acerca de tomar la opción -overloadaggressively si notas un comportamiento extraño, pero hasta donde sé, no estoy usando esa opción.

¿Alguien más ha experimentado misteriosos NPE usando android y proguard? ¿Hay alguna otra configuración que las personas puedan recomendar para reducir las optimizaciones que podrían estar causando este problema?

¿Alguna otra idea?

Para referencia, aquí es la función unobfuscated que está recibiendo la 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(); 


} 

lo que la única posibilidad que puedo ver son juego, despachador y los ajustes.

juego se establece a través de este código en otra clase. juego es una variable final en esa otra clase:

game.setScreen(new MainMenuScreen(game)); 

despachador se establece dentro de la llamada a super arriba.

getSettings() devuelve un objeto de configuración que se establece desde el inicio de la aplicación, es privado y nunca se desarma. También se usa antes de este método varias veces.

No hay primitivas de auto-boxing.

aquí es la configuración 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 *; 
} 
+0

¿Pueden por favor publicar el código que causa la configuración de NPE y ProGuard que usó? – Idolon

+0

Agregué más detalles sobre el código específico y mi configuración proguard – Paul

Respuesta

2

Su mejor opción sería utilizar el archivo mapping.txt y la herramienta de retroceso para encontrar la ubicación exacta del error. A partir de ahí, sería más fácil de entender si es realmente Proguard o algún otro caso final en el que no haya pensado.

Para ello, es necesario copiar el seguimiento de la pila desde la consola del desarrollador a otro archivo, vamos a suponer que se llama

c: trace.txt

Ahora, en su proyecto \ , encontrarás una carpeta Proguard con 4 archivos. Supongamos que su proyecto está en

c: \ proyecto

Lo que necesitan hacer es ejecutar la herramienta de retroceso (utilizando el archivo por lotes para facilitar su uso) situado en (cambio en el ubicación de la carpeta Android SDK):

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

Ir a esa carpeta y ejecutar:

retrazo c: \ proyecto \ Proguard \ mapping.txt c: \ trace.txt

A partir de ahí, que sería mucho más fácil de entender nuestra la exacta línea de la excepción y probablemente encuentre el error.

Según mi experiencia, las únicas cosas que pueden estropearse son las bibliotecas de terceros. El código normal de Android, para todos mis proyectos, nunca sufrió daños por ofuscación.

+0

Sí, ya lo he hecho. Sé el método exacto y todo el seguimiento de la pila que llamó a ese método. Lo único que no sé es el número de línea, pero esto se debe a que se trata de una versión de producción sin números de línea. Además, por lo que entiendo, Proguard realmente reescribe parte de tu código durante su fase de optimización (que es una de las razones por las cuales sospecho que es proguardista). Así que fui relegado a la depuración de moda antigua de cada línea que podría producir un NPE, pero, por desgracia, no hay dados. – Paul

+0

Así que el único consejo que tengo para usted es poner una versión no difundida durante unos días y ver si recibe informes de errores adicionales ...Nunca encontré errores con Proguard en mi código (desde las aplicaciones más complejas hasta las más simples) y siempre ofusco mis proyectos. – IncrediApp

+0

Sí, estaba preocupado de que tenga que llegar a eso, pero desafortunadamente no voy a dejar que mi código salga para que todos lo vean. He visto algunos otros mensajes hablar sobre problemas con el código proguard en teléfonos móviles: http://stackoverflow.com/questions/93290/best-java-obfuscation-application-for-size-reduction Así que no creo que esto no tenga precedentes . Solo esperaba que otras personas tuvieran circunstancias similares. – Paul

1

Lo siento, todavía no puedo publicar comentarios (soy nuevo en SO).

Este podría ser otro problema que he encontrado. En algunos teléfonos, en algún momento hubo un problema con las bibliotecas Android que faltaban, como una biblioteca JSon.

Le recomiendo que eche un vistazo más de cerca a los teléfonos que realmente obtienen la NPE, puede haber algunas similitudes.

En mi caso, era el HTC Desire Z, que faltaba la biblioteca JSon y la fuerza de la aplicación se cerraba cada vez que se llamaba a la parte JSon. El problema fue resuelto por HTC más tarde con una revisión de la ROM de Desire Z.

8

Bien, creo que llegué a la raíz del problema/confusión.

Una de las cosas que proguard hace es en línea algunos métodos. Debido a esto, todo el contenido de mi función setupContinue() en la parte inferior de mi constructor se agregó directamente a los contenidos de mi constructor. Así que ahora tengo un montón más código para revisar, y veo algunas posibilidades más para los NPE. Estoy bastante seguro de que llegaré al fondo de mi problema.

Me di cuenta de esto tomando el ofuscado.jar que Proguard produce y ejecutándolo a través de un descompilador. Es un ejercicio interesante a medida que obtienes más información sobre el funcionamiento interno de Proguard. Lo recomiendo encarecidamente a las personas que deseen comprender mejor los impactos que proguard tiene en su código.

+0

Ver el código procesado puede ser muy útil. Debe asegurarse de estar utilizando la última versión de ProGuard (4.6 o 4.7 beta, en este momento). En particular, la versión 4.6 contiene una corrección relacionada con los inicializadores estáticos que no se ejecutan debido a los métodos que se mueven (en línea). Aún así, es sorprendente que el problema no ocurra de manera consistente. –

+0

La parte sobre el código de guarnición Proguard fue de gran ayuda. Ahora me doy cuenta de por qué mi usuario envió rastros de bloque/pila que parecen terminar en una firma de método en lugar de la línea real de código que causa el NPE. Es una lástima que no apunte específicamente a la línea que causa el problema; realmente apestaría si el método contuviera una tonelada de código (como encontrar una aguja en un pajar). –

Cuestiones relacionadas