2015-09-11 5 views
8

Ich weiß nicht einmal, welchen Titel ich geben soll, es ist so seltsam. Ich habe ein kleines Android logic puzzle gebaut, das Farbwerte in einem ARGB-Ganzzahlformat verwendet. Um die Farben für die Animation zu mischen, wenn Sie einen Level beenden, ich habe die folgende Funktion:Der seltsamste Android-Bug aller Zeiten - Vielleicht ein ProGuard-Problem?

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

Ich nenne diese Funktion während der Animation mit diesem Code (enthält Debug-print-Anweisung):

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)); 

Hier ist pos einfach ein Doppelwert, der von 0,0 bis 1,0 zählt.

Wenn ich diesen Code direkt aus dem Eclipse über das Android-Entwickler-Plug-in auf Eclipse ausführe, funktioniert alles einwandfrei.

ABER: Wenn ich die App-Paket und installieren Sie die APK, es zuverlässig vermasselt, was mir eine Ausgabe wie folgt aus:

... 
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 diesem Beispiel bis Position 0,89, alles in Ordnung ist. Dann beginnen die Dinge zu oszillieren zwischen Arbeit und Verschrauben nur der R-Komponente (auf 0 gesetzt; es ist immer die R-Komponente, die vermasselt), und schließlich, beginnend bei 0,93 in diesem Beispiel, Dinge immer vermasseln. Und dann, wenn ich genau die gleiche Animation wieder laufen lasse, fängt es an, sofort zu verschrauben ...

Wie in aller Welt ist das möglich? Verletzt dieser ProGuard meinen Code? Wenn das eine Möglichkeit ist, gibt es einen Weg, es herauszufinden? Ich habe hier wirklich einen Verlust an Ideen ... Und wie kann es probabilistisch sein, ob es funktioniert oder nicht? Oder verpasse ich hier etwas völlig Offensichtliches?

Wenn es ein ProGuard-Problem sein könnte, welche Art von Optimierungen könnte diesen Teil des Codes beeinflussen? Gibt es eine Liste von Schaltern, die ich nacheinander ausschalten könnte, um den Flockigen zu finden?

UPDATE:

Meine project.properties Datei sieht wie folgt aus (kommentierten Zeilen gelöscht):

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

und proguard-project.txt wie folgt aus:

-flattenpackagehierarchy 
-renamesourcefileattribute SourceFile 
-keepattributes SourceFile,LineNumberTable 

proguard-android.txt im SDK-Verzeichnis sollte nach wie vor wie es mit den SDK Tools v24.1.2 ausgeliefert wurde (vorausgesetzt, dass ist das Paket, das ProGuard enthält ...; wieder ohne Kommentare):

-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:

Ich glaube, ich die kompilierte Ausgabe von dem, was ProGuard hat mit dem blend -Methode in der dump.txt Datei gefunden:

+ 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) 

UPDATE 3:

Ich versuchte es neu zu schreiben, das Mischverfahren zu dieser (diese Idee zu sein, wenn ich alle Komponenten die gleiche Behandlung, ist es nicht möglich sein sollte, nur eine mehr vermasseln):

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

nicht überraschend, dieser Code funktioniert jetzt nur so wie es sollte! Aber das bringt mich immer noch nicht näher zu verstehen, warum der ursprüngliche Code versagt hat und an welchen anderen Stellen meiner App etwas ähnlich Triviales auf unerwartete Weise scheitern könnte.

UPDATE 4:

einfach die Linien Neuordnungs dazu auch das Problem behebt:

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

Es muss einige lokale Variablen-Wiederverwendung Sache sein, ich weiß nur nicht, warum das ist nicht ersichtlich aus der dump.txt-Datei oben ... Ist das etwas, was Dalvik tut (aber nur für APKs!?!)?

+0

Warum spammen Sie mit einem Link zu Ihrer App? Wie ist dieser Link mit der Frage verbunden? – Selvin

+0

@Selvin Wenn Sie sehen möchten, dass der Fehler passiert (die Farbe des wachsenden Sterns) ... Auch finde ich es normalerweise interessant zu sehen, was Leute mit meinen Antworten auf SO machen ... Aber ich kann es löschen, wenn es ist unpassend ... –

+0

Könnten Sie Progard-Datei Informationen darüber geben, welche Proguard-Datei Sie eventuell verwenden? – Selvin

Antwort

0

Ich habe keine Antwort über das Proguard-Zeug, aber es gibt einige Color Hilfsmethoden, die, aus welchem ​​Grund auch immer, Ihnen das richtige Ergebnis geben können. Zumindest wird es Ihren Code lesbarer machen. Versuchen Sie folgendes:

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

Ich frage mich, ob dies tatsächlich beheben würde, da die Methoden im Wesentlichen das gleiche tun, was mein Code tut (siehe https://github.com/android/platform_frameworks_base/blob/master/graphics/java/android/graphics/Color. Java). Also, wenn sie inline werden, würde es im Grunde wieder derselbe Code sein (mit einem anderen Shift-Operator, der in einigen Fällen verwendet wird und ein paar & 0xFFs fallengelassen wurde). Ich werde es versuchen, nur zum Spaß ... eine Sekunde ... –

+0

Ich denke, es ist einen Versuch wert. Vielleicht wird Proguard sich nicht mit ihnen anlegen, da sie im SDK sind und es irgendwie mit eurem Programm zu tun hat. –

+0

Wow! Das hat das Problem NICHT gelöst! Obwohl ich meine eigene "Color.java" mit der Quelle erstellen musste, die ich oben in meinem Verzeichnis verlinkt habe, da sich mein Code tatsächlich in einer plattformübergreifenden Bibliothek befindet, die die Android-Jars nicht importiert! –

1

Wirklich interessant Problem zu untersuchen und lösen es wird auf jeden Fall Ihre Kompetenzen erhöhen mit (möglicherweise) ProGuard, sondern um das Gesamtbild zu betrachten, würde ich empfehlen, mit vorhandenen Werkzeugen gehen für animierende Farbänderungen :)

ArgbEvaluator (oder ValueAnimator#ofArgb(int...) für API 21+) zur Rettung!

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(); 

Es ermöglicht Ihnen, es zu zwicken, wie Sie benötigen (verschiedene Interpolatoren, Verzögerungen, Hörer, usw.) und auch da es Von der Plattform kommend besteht eine große Chance, dass ProGuard es nicht berührt

PS. Ich möchte immer noch die Ursache für das Problem sehen, das Sie erleben :)

+0

:) Während dies eine Option sein könnte, versuche ich, so viele Geräte wie möglich vernünftig zu unterstützen. Also, ich habe derzeit keine Abhängigkeiten in meinem Code über API-Level 7. Auch dieser Code ist so dumm einfach, es sollte wirklich nur funktionieren ... –

+0

gut, intern macht es ähnliche Dinge, was Sie tun. Sehen Sie sich Ihre anderen Kommentare an - es scheint, als ob es sich um eine Cross-Platform-Bibliothek handelt und Sie nicht auf Android-APIs verweisen können. Daher würde ich annehmen, dass diese Lösung nicht nur für Sie funktioniert. –

+0

Das stimmt auch, ja. :) Danke für den Vorschlag. Aber das hat mich jetzt wirklich beunruhigt! Wenn etwas so Einfaches wie so sehr vermasseln kann, wie kann ich dann meinem anderen Code vertrauen? –