我都不知道给这个起什么名字,太奇怪了。我构建了一个小的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.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.**
更新 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!?!)?