有史以来最怪异的Android bug - 也许是ProGuard问题?

Mar*_* A. 8 android android-proguard

我甚至不知道给这个题什么题,这太奇怪了.我构建了一个小的Android逻辑谜题,它使用ARGB整数格式的颜色值.要在完成关卡时混合动画的颜色,我有以下功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

我在动画期间使用此代码调用此函数(包括调试打印语句):

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));
Run Code Online (Sandbox Code Playgroud)

这里pos只是一个从0.0到1.0的双值.

如果我通过Android开发人员插件从Eclipse中直接在我的手机上运行此代码,一切正常.

但是:如果我打包应用程序并安装APK,它可靠地拧紧,给我类似的输出:

...
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
...
Run Code Online (Sandbox Code Playgroud)

在这个例子中,直到位置0.89,一切都很好.然后,事情开始在工作和拧紧R组件之间振荡(设置为0;它总是被拧紧的R组件),并且最终,从这个示例中的0.93开始,事情总是搞砸了.然后,如果我再次运行完全相同的动画,它会立即开始搞砸......

这怎么可能呢?ProGuard是否在搞乱我的代码?如果这是可能的,有没有办法确定?我真的在这里失去了想法......无论它是否有效,它怎么可能是概率的?或者我只是错过了一些完全明显的东西?

如果它可能是ProGuard问题,那么哪种优化会影响这部分代码?是否有一个开关列表,我可以尝试逐个关闭以找到片状的?

更新:

我的project.properties文件看起来像这样(删除了注释行):

proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
target=android-22
Run Code Online (Sandbox Code Playgroud)

proguard-project.txt这样:

-flattenpackagehierarchy
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
Run Code Online (Sandbox Code Playgroud)

proguard-android.txt 在SDK目录中应该仍然像SDK Tools v24.1.2一样(假设包含ProGuard的包......;再次排除注释):

-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.**
Run Code Online (Sandbox Code Playgroud)

更新2:

我想我找到了ProGuard blenddump.txt文件中使用-method 做的编译输出:

+ 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)
Run Code Online (Sandbox Code Playgroud)

更新3:

我试图将混合方法重写为此(想法是如果我对所有组件都一样,那么就不可能再搞一个了):

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;
}
Run Code Online (Sandbox Code Playgroud)

毫不奇怪,这段代码现在可以正常运行了!但是这仍然没有让我更接近理解为什么原始代码失败以及在我的应用程序的其他地方同样琐碎的事情可能以意想不到的方式失败.

更新4:

简单地将线重新排序也可以解决问题:

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;
}
Run Code Online (Sandbox Code Playgroud)

它必须是一些局部变量重用的东西,我只是不知道为什么从上面的dump.txt文件中看不出来......这是Dalvik做的事情(但只有签名的APK!?!)?

Jam*_*ken 0

我没有关于混淆器的答案,但是有一些Color辅助方法,无论出于何种原因,都可以给你正确的结果。至少,它会让你的代码更具可读性。尝试这个:

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);
}
Run Code Online (Sandbox Code Playgroud)