8

我都不知道给这个起什么名字,太奇怪了。我构建了一个小的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;
}

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

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

proguard-android.txtSDK 目录中的内容仍应与 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:

blend我想我在文件中找到了 ProGuard 对 -method所做的编译输出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

我试图将 blend 方法重写为此(想法是,如果我将所有组件都视为相同,则不可能再搞砸一个):

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!?!)?

4

2 回答 2

1

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

ArgbEvaluator(或API 21+ 的ValueAnimator#ofArgb(int...))来救援!

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。我仍然真的很想看看你遇到的问题的根本原因:)

于 2015-09-12T00:19:31.307 回答
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);
}
于 2015-09-12T00:09:22.860 回答