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

时间:2015-09-11 23:03:05

标签: android android-proguard

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

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

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

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

在这里,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
...

在这个例子中,直到位置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

proguard-project.txt是这样的:

-flattenpackagehierarchy
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
SDK目录中的

proguard-android.txt仍然应该与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.**

更新2:

我想我找到了ProGuard在blend文件中使用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)

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

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

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

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

2 个答案:

答案 0 :(得分:1)

调查和解决它的真正有趣的问题肯定会提高你(可能)ProGuard的专业水平,但为了看大局,我建议使用现有工具来设置颜色变化动画:)

ArgbEvaluator(或{3}}获得API 21+)救援!

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

它允许你根据需要调整它(不同的内插器,延迟,听众等),并且因为它来自平台,ProGuard很有可能不会触摸它

PS。我仍然非常希望看到您遇到的问题的根本原因:)

答案 1 :(得分:0)

我没有关于proguard内容的答案,但是有一些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);
}