2011-10-02 6 views
6

Ho distribuito un'applicazione sul Marketplace Android. Ricevo nuovamente segnalazioni di errori da una manciata di utenti (forse il 2%) dove stanno ottenendo NullPointerExceptions dove non ha senso logico.Il codice offuscato proguard Android sta causando NullPointerException quando non dovrebbe essere

Non sono mai stato in grado di replicare da solo. Il codice è relativamente semplice ed è un percorso di codice comune che OGNI utente deve seguire. In realtà ho preso ogni singola riga di codice che potrebbe creare l'NPE e l'ho racchiusa in un blocco try-catch e lanciare un'eccezione di runtime personalizzata, ma sto ancora ricevendo errori di NullPointerException non rilevati.

A questo punto, l'unica cosa che posso immaginare sarebbe qualcosa che riguarda la mia confusione Proguard. Ho visto qualche altro articolo che parla dell'eliminare l'opzione -overloadaggressively se si nota un comportamento strano, ma per quanto posso dire, non sto usando questa opzione.

Qualcun altro ha sperimentato misteriosi NPE utilizzando Android e proguard. Esistono altre impostazioni che le persone possono consigliare per azzerare le ottimizzazioni che potrebbero causare questo problema?

Altre idee?

Per avere un riferimento, qui è la funzione unobfuscated che sta ottenendo 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(); 


} 

così l'unica possibilità che vedo sono gioco, dispatcher e le impostazioni.

il gioco viene impostato tramite questo codice in un'altra classe. gioco è una variabile finale che altra classe:

game.setScreen(new MainMenuScreen(game)); 

dispatcher viene impostata all'interno nella chiamata a super sopra.

getSettings() restituisce un oggetto impostazioni che viene impostato all'inizio dell'applicazione, privato e mai disinserito. È anche usato prima di questo metodo più volte.

Non ci sono primitive di auto-boxing.

qui è il config 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

È possibile inserire il codice che causa le impostazioni NPE e ProGuard utilizzate? – Idolon

+0

Ho aggiunto ulteriori dettagli sul codice specifico e sulla mia proguard config – Paul

risposta

2

La cosa migliore sarebbe quella di utilizzare il file mapping.txt e lo strumento ritracciamento per trovare la posizione esatta dell'errore. Da lì sarebbe più facile capire se è davvero Proguard o qualche altro end-case a cui non hai pensato.

Per fare questo, è necessario copiare la traccia dello stack dal console dello sviluppatore a un altro file, supponiamo che si chiama

c: \ trace.txt

Ora, nel progetto , troverai una cartella Proguard con 4 file. Supponiamo che il progetto è in

c: \ progetto

Quello che è necessario fare è eseguire lo strumento ritracciamento (utilizzando il file batch per l'utilizzo più semplice) che si trova al numero (modifica al percorso della cartella Android SDK):

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

Vai a quella cartella ed eseguire:

ritracciamento c: \ project \ Proguard \ mapping.txt c: \ trace.txt

Da lì in poi, sarebbe molto più facile capire la nostra l'esatto linea dell'eccezione e probabilmente trovare l'errore.

Dalla mia esperienza, le uniche cose che potrebbero essere incasinate sono le librerie di terze parti. Il normale codice Android, per tutti i miei progetti, non è mai stato danneggiato dall'offuscamento.

+0

Sì, l'ho già fatto. Conosco il metodo esatto e l'intera traccia dello stack che ha chiamato quel metodo. L'unica cosa che non conosco è il numero di linea, ma questo perché si tratta di una versione di produzione senza numeri di riga. Inoltre, da quanto ho capito, Proguard riscrive effettivamente parte del tuo codice durante la sua fase di ottimizzazione (che è uno dei motivi per cui sospetto proguard). Così sono stato relegato al debugging vecchio stile di ogni linea che poteva produrre un NPE, ma ahimè, niente da fare. – Paul

+0

Quindi l'unico consiglio che ho per te è quello di mettere una versione non offuscata per alcuni giorni e vedere se ottieni ulteriori segnalazioni di errore ...Non ho mai riscontrato errori con Proguard nel mio codice (dalle app più complesse ai più semplici) e offusco sempre i miei progetti. – IncrediApp

+0

Sì, ero preoccupato che potesse dover arrivare a quello, ma sfortunatamente non ho intenzione di lasciare il mio codice allo stato brado perché tutti lo vedano. Ho visto alcuni altri post parlare di problemi con il codice proguard sui telefoni cellulari: http://stackoverflow.com/questions/93290/best-java-obfuscation-application-for-size-reduction Quindi non penso che questo sia senza precedenti . Speravo solo che altre persone avessero avuto circostanze simili. – Paul

1

Siamo spiacenti non posso ancora postare commenti (sono nuovo di SO).

Questo potrebbe essere un altro problema che ho riscontrato. Su alcuni telefoni ci fu un problema ad un certo punto con le librerie Android mancanti come una libreria JSon.

Consiglierei di dare un'occhiata più da vicino a quali telefoni effettivamente ricevono l'NPE - potrebbero esserci alcune somiglianze.

Nel mio caso era l'HTC Desire Z, che mancava la libreria JSon e quindi la forza dell'app si chiudeva ogni volta che veniva chiamata la parte JSon. Il problema è stato risolto da HTC in seguito con un aggiornamento rapido alla rom del Desiderio Z.

8

OK, penso di essere arrivato alla radice del problema/confusione.

Una delle cose che proguard fa è in linea alcuni metodi. Per questo motivo, l'intero contenuto della mia funzione setupContinue() nella parte inferiore del mio costruttore è stato aggiunto direttamente al contenuto del mio costruttore. Così ora ho un mucchio di codice in più da recensire, e vedo altre possibilità per gli NPE. Sono abbastanza sicuro che raggiungerò il fondo del mio problema.

L'ho capito prendendo il file obfuscated.jar che produce proguard e eseguendolo attraverso un decompilatore. È un esercizio interessante quando si ottiene un po 'più di comprensione del funzionamento interno di proguard. Lo consiglio vivamente a chi vuole capire meglio l'impatto che proguard ha sul proprio codice.

+0

Guardare il codice elaborato può essere molto utile. Assicurati di utilizzare l'ultima versione di ProGuard (4.6 o 4.7 beta, al momento). In particolare, la versione 4.6 contiene una correzione relativa agli inizializzatori statici che non vengono eseguiti a causa dei metodi che vengono spostati (in linea). Tuttavia, è sorprendente che il problema non avvenga in modo coerente. –

+0

La parte relativa al codice di in-lining di Proguard è stata di grande aiuto. Ora mi rendo conto del motivo per cui il mio utente ha inviato tracce di crash/stack sembra terminare con una firma del metodo anziché con la riga di codice che causa l'NPE. Peccato che non punti in modo specifico alla linea che causa il problema; farebbe davvero schifo se il metodo contenesse una tonnellata di codice (come trovare un ago in un pagliaio). –