2015-09-11 4 views
8

Non so nemmeno quale titolo dare a questo, è così strano. Ho creato un piccolo Android logic puzzle che utilizza i valori dei colori in un formato intero ARGB. Per fondere i colori per l'animazione quando si completa un livello, ho la seguente funzione:Bug più strano di sempre in Android - Forse un problema con ProGuard?

public static int blend(int color1, int color2, double position) { 
    if (position<0) position=0; 
    if (position>1) position=1; 
    int a = (color1 >>> 24) & 0xFF; 
    int r = (color1 >>> 16) & 0xFF; 
    int g = (color1 >>> 8) & 0xFF; 
    int b = color1   & 0xFF; 

    int da = ((color2 >>> 24) & 0xFF) - a; 
    int dr = ((color2 >>> 16) & 0xFF) - r; 
    int dg = ((color2 >>> 8) & 0xFF) - g; 
    int db = (color2   & 0xFF) - b; 

    a += da * position; 
    r += dr * position; 
    g += dg * position; 
    b += db * position; 

    return (a<<24) | (r<<16) | (g<<8) | b; 
} 

Io chiamo questa funzione durante l'animazione con questo codice (include dichiarazione di debug di stampa):

int color = blend(START_COLOR, END_COLOR, pos*pos*pos*pos*pos); 
System.out.println(Integer.toHexString(START_COLOR)+", "+Integer.toHexString(END_COLOR)+", "+pos+" -> "+Integer.toHexString(color)); 

Qui, pos è semplicemente un doppio valore che conta da 0.0 a 1.0.

Se eseguo questo codice direttamente sul mio telefono da Eclipse tramite il plugin per sviluppatori Android, tutto funziona perfettamente.

MA: Se Che tipo di imballaggio l'applicazione e installare l'APK, IT affidabile viti in su, dandomi un output simile a questo:

... 
fff9b233, f785a307, 0.877 -> fabcaa1c 
fff9b233, f785a307, 0.881 -> fabbaa1b 
fff9b233, f785a307, 0.883 -> fabaa91b 
fff9b233, f785a307, 0.886 -> fab9a91a 
fff9b233, f785a307, 0.89 -> fab8a91a 
fff9b233, f785a307, 0.891 -> fa00a91a 
fff9b233, f785a307, 0.895 -> fab6a919 
fff9b233, f785a307, 0.896 -> fa00a919 
fff9b233, f785a307, 0.901 -> fab4a918 
fff9b233, f785a307, 0.901 -> fab4a918 
fff9b233, f785a307, 0.907 -> fab1a817 
fff9b233, f785a307, 0.907 -> fab1a817 
fff9b233, f785a307, 0.912 -> f9afa817 
fff9b233, f785a307, 0.913 -> f900a817 
fff9b233, f785a307, 0.919 -> f9aca816 
fff9b233, f785a307, 0.919 -> f9aca816 
fff9b233, f785a307, 0.925 -> f9aaa715 
fff9b233, f785a307, 0.925 -> f9aaa715 
fff9b233, f785a307, 0.93 -> f900a714 
fff9b233, f785a307, 0.931 -> f900a714 
fff9b233, f785a307, 0.936 -> f900a713 
fff9b233, f785a307, 0.937 -> f900a713 
fff9b233, f785a307, 0.942 -> f900a612 
fff9b233, f785a307, 0.942 -> f900a612 
fff9b233, f785a307, 0.947 -> f800a611 
fff9b233, f785a307, 0.948 -> f800a611 
fff9b233, f785a307, 0.954 -> f800a610 
fff9b233, f785a307, 0.954 -> f800a610 
fff9b233, f785a307, 0.959 -> f800a50f 
... 

In questo esempio, fino a posizione di 0.89, è tutto a posto. Quindi, le cose iniziano a oscillare tra il lavorare e avvitare solo il componente R (impostato su 0, è sempre il componente R che si fa rovinare), e alla fine, a partire da 0.93 in questo esempio, le cose vanno sempre male. E poi, se eseguo di nuovo la stessa identica animazione, inizia a rovinare subito ...

Com'è possibile? ProGuard è in preda al mio codice? Se questa è una possibilità, c'è un modo per scoprirlo di sicuro? Sono davvero a corto di idee qui ... E come può essere probabilistico se funzioni o meno? O sto semplicemente perdendo qualcosa di totalmente ovvio qui?

Se può trattarsi di un problema di ProGuard, che tipo di ottimizzazioni potrebbero influire su questa parte del codice? C'è una lista di interruttori che potrei provare a spegnere uno per uno per trovare quello traballante?

UPDATE:

Il mio file project.properties si presenta così (righe di commento cancellate):

proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 
target=android-22 

e proguard-project.txt come questo:

-flattenpackagehierarchy 
-renamesourcefileattribute SourceFile 
-keepattributes SourceFile,LineNumberTable 

proguard-android.txt nella directory SDK dovrebbe essere ancora essere proprio come è stato fornito con SDK Tools v24.1.2 (supponendo che è il pacchetto che include ProGuard ...; ancora una volta esclusi i commenti):

-dontusemixedcaseclassnames 
-dontskipnonpubliclibraryclasses 
-verbose 
-dontoptimize 
-dontpreverify 

-keepattributes *Annotation* 
-keep public class com.google.vending.licensing.ILicensingService 
-keep public class com.android.vending.licensing.ILicensingService 

-keepclasseswithmembernames class * { 
    native <methods>; 
} 

-keepclassmembers public class * extends android.view.View { 
    void set*(***); 
    *** get*(); 
} 

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

-keepclassmembers class **.R$* { 
    public static <fields>; 
} 

-dontwarn android.support.** 

UPDATE 2:

Credo di aver trovato l'uscita di quello compilato ProGuard fa con il blend -Metodo nel file dump.txt:

+ Method:  a(IID)I 
    Access flags: 0x9 
    = public static int a(int,int,double) 
    Class member attributes (count = 1): 
    + Code attribute instructions (code length = 171, locals = 12, stack = 6): 
    [0] dload_2 v2 
    [1] dconst_0 
    [2] dcmpg 
    [3] ifge +5 (target=8) 
    [6] dconst_0 
    [7] dstore_2 v2 
    [8] dload_2 v2 
    [9] dconst_1 
    [10] dcmpl 
    [11] ifle +5 (target=16) 
    [14] dconst_1 
    [15] dstore_2 v2 
    [16] iload_0 v0 
    [17] bipush 24 
    [19] iushr 
    [20] sipush 255 
    [23] iand 
    [24] istore v4 
    [26] iload_0 v0 
    [27] bipush 16 
    [29] iushr 
    [30] sipush 255 
    [33] iand 
    [34] istore v5 
    [36] iload_0 v0 
    [37] bipush 8 
    [39] iushr 
    [40] sipush 255 
    [43] iand 
    [44] istore v6 
    [46] iload_0 v0 
    [47] sipush 255 
    [50] iand 
    [51] istore v7 
    [53] iload_1 v1 
    [54] bipush 24 
    [56] iushr 
    [57] sipush 255 
    [60] iand 
    [61] iload v4 
    [63] isub 
    [64] istore v8 
    [66] iload_1 v1 
    [67] bipush 16 
    [69] iushr 
    [70] sipush 255 
    [73] iand 
    [74] iload v5 
    [76] isub 
    [77] istore v9 
    [79] iload_1 v1 
    [80] bipush 8 
    [82] iushr 
    [83] sipush 255 
    [86] iand 
    [87] iload v6 
    [89] isub 
    [90] istore v10 
    [92] iload_1 v1 
    [93] sipush 255 
    [96] iand 
    [97] iload v7 
    [99] isub 
    [100] istore v11 
    [102] iload v4 
    [104] i2d 
    [105] iload v8 
    [107] i2d 
    [108] dload_2 v2 
    [109] dmul 
    [110] dadd 
    [111] d2i 
    [112] istore v4 
    [114] iload v5 
    [116] i2d 
    [117] iload v9 
    [119] i2d 
    [120] dload_2 v2 
    [121] dmul 
    [122] dadd 
    [123] d2i 
    [124] istore v5 
    [126] iload v6 
    [128] i2d 
    [129] iload v10 
    [131] i2d 
    [132] dload_2 v2 
    [133] dmul 
    [134] dadd 
    [135] d2i 
    [136] istore v6 
    [138] iload v7 
    [140] i2d 
    [141] iload v11 
    [143] i2d 
    [144] dload_2 v2 
    [145] dmul 
    [146] dadd 
    [147] d2i 
    [148] istore v7 
    [150] iload v4 
    [152] bipush 24 
    [154] ishl 
    [155] iload v5 
    [157] bipush 16 
    [159] ishl 
    [160] ior 
    [161] iload v6 
    [163] bipush 8 
    [165] ishl 
    [166] ior 
    [167] iload v7 
    [169] ior 
    [170] ireturn 
    Code attribute exceptions (count = 0): 
    Code attribute attributes (attribute count = 2): 
    + Line number table attribute (count = 15) 
     [0] -> line 33 
     [8] -> line 34 
     [16] -> line 35 
     [26] -> line 36 
     [36] -> line 37 
     [46] -> line 38 
     [53] -> line 40 
     [66] -> line 41 
     [79] -> line 42 
     [92] -> line 43 
     [102] -> line 45 
     [114] -> line 46 
     [126] -> line 47 
     [138] -> line 48 
     [150] -> line 50 
    + Stack map table attribute (count = 2): 
     - [8] Var: ..., Stack: (empty) 
     - [16] Var: ..., Stack: (empty) 

AGGIORNAMENTO 3:

Ho provato riscrivere il metodo blend a questo (idea è che se I trattare tutti i componenti stessi, non dovrebbe essere possibile avvitare un solo più):

public static int blend(int color1, int color2, double position) { 
    if (position<0) position=0; 
    if (position>1) position=1; 

    int result = 0; 

    for (int shift = 0; shift<32; shift += 8) { 
     int component = (color1 >>> shift) & 0xFF; 
     int change = ((color2 >>> shift) & 0xFF) - component; 
     component += change * position; 
     result |= component << shift; 
    } 
    return result; 
} 

Non sorprendentemente, questo codice funziona ora, solo come dovrebbe! Ma questo non mi aiuta ancora a capire perché il codice originale ha fallito e in quali altri punti della mia app qualcosa di simile banale potrebbe fallire in modi inaspettati.

AGGIORNAMENTO 4:

semplicemente riordinare le linee per questo risolve anche il problema:

public static int blend(int color1, int color2, double position) { 
    if (position<0) position=0; 
    if (position>1) position=1; 

    int a = (color1 >>> 24) & 0xFF; 
    int da = ((color2 >>> 24) & 0xFF) - a; 
    a += da * position; 

    int r = (color1 >>> 16) & 0xFF; 
    int dr = ((color2 >>> 16) & 0xFF) - r; 
    r += dr * position; 

    int g = (color1 >>> 8) & 0xFF; 
    int dg = ((color2 >>> 8) & 0xFF) - g; 
    g += dg * position; 

    int b = color1   & 0xFF; 
    int db = (color2   & 0xFF) - b; 
    b += db * position; 

    return (a<<24) | (r<<16) | (g<<8) | b; 
} 

che debba essere una cosa locale-variabile riutilizzo, solo che non so perché che non è evidente dal file dump.txt sopra ... È qualcosa che Dalvik fa (ma solo agli APK firmati!?!)?

+0

Perché stai facendo spamming con il link alla tua app? In che modo questo link è collegato alla domanda? – Selvin

+0

@Selvin Nel caso in cui si voglia vedere l'errore (il colore della stella in crescita) ... Inoltre, di solito trovo interessante vedere cosa fanno le persone con le mie risposte su SO ... Ma posso cancellarlo se è inappropriato ... –

+0

È possibile fornire informazioni sul file proguard su quale file di proguard è eventualmente utilizzato in modo predefinito? – Selvin

risposta

0

Non ho una risposta sul materiale proguard, ma ci sono alcuni metodi di supporto Color che possono, per qualsiasi motivo, fornire il risultato corretto. Per lo meno, renderà il tuo codice più leggibile. Prova questo:

public static int blend(int color1, int color2, double position) { 
    if (position < 0) { 
     position = 0; 
    } 
    if (position > 1) { 
     position = 1; 
    } 

    int a = Color.alpha(color1); 
    int r = Color.red(color1); 
    int g = Color.green(color1); 
    int b = Color.blue(color1); 

    int da = Color.alpha(color2) - a; 
    int dr = Color.red(color2) - r; 
    int dg = Color.green(color2) - g; 
    int db = Color.blue(color2) - b; 

    a += da * position; 
    r += dr * position; 
    g += dg * position; 
    b += db * position; 

    return Color.argb(a, r, g, b); 
} 
+0

Mi chiedo se questo potrebbe effettivamente risolverlo, poiché i metodi essenzialmente fanno esattamente la stessa cosa che fa il mio codice (vedi https://github.com/android/platform_frameworks_base/blob/master/graphics/java/android/graphics/Color. Giava). Quindi, se sono in linea, sarebbe di nuovo lo stesso codice (con un diverso operatore di shift usato in alcuni casi e alcuni e 0xFF sono stati eliminati). Ci proverò, solo per il gusto di farlo ... un secondo ... –

+0

Credo che valga la pena provarci. Forse proguard non si scherza con loro dal momento che sono nell'SDK ed è in qualche modo in disordine con il tuo –

+0

Wow! Questo NON ha risolto il problema! Anche se dovevo creare il mio "Color.java" usando la fonte che ho linkato sopra nella mia directory, dal momento che il mio codice è in realtà in una libreria multipiattaforma che non importa i jar Android! –

1

problema davvero interessante indagare e risolvere sarà sicuramente aumentare il vostro livello di esperienza con (possibilmente) ProGuard, ma per il bene di guardare il quadro più ampio, consiglierei di andare con gli strumenti esistenti per animazione dei cambiamenti di colore :)

ArgbEvaluator (o ValueAnimator#ofArgb(int...) per API 21+) per il salvataggio!

API 21+:

ValueAnimator animator = ValueAnimator.ofArgb(0xFFFF0000, 0xFF00FF00); //red->green 

API 11+:

ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), 0xFFFF0000, 0xFF00FF00); //red->green 
animator.setDuration(1000);//1second 
animator.start(); 

Esso consente di modificarlo come avete bisogno (diversi interpolatori, ritardi, ascoltatori, ecc) e anche dal momento che è arrivando dalla piattaforma c'è una grande possibilità che ProGuard non la tocchi

PS. Voglio davvero vedere la causa principale del problema che stai riscontrando :)

+0

:) Anche se questa potrebbe essere un'opzione, sto cercando di supportare il numero di dispositivi sensibilmente possibile. Quindi, al momento non ho alcuna dipendenza dal mio codice oltre il livello API 7. Inoltre, questo codice è così stupidamente semplice, dovrebbe funzionare solo ... –

+0

beh, internamente fa cose simili a quello che stai facendo. Guardando i tuoi altri commenti - sembra che questa sia una lib multipiattaforma e non puoi fare riferimento alle API di Android, quindi suppongo che questa soluzione non funzioni semplicemente per te. –

+0

Anche questo è vero, sì. :) Grazie per il suggerimento, però. Ma questo mi ha davvero preoccupato ora! Se qualcosa di così semplice può rovinare così tanto, come posso fidarmi di qualcuno del mio altro codice? –