これにどんなタイトルを付けたらいいのかわからない、とても奇妙です。ARGB 整数形式の色の値を使用するAndroid ロジック パズルを作成しました。レベルを完了したときにアニメーションの色をブレンドするために、次の関数があります。
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;
}
このコードを使用して、アニメーション中にこの関数を呼び出します (debug print ステートメントを含む)。
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 までカウントする単純な double 値です。
Android 開発者プラグインを介して Eclipse 内からこのコードを携帯電話で直接実行すると、すべて正常に動作します。
BUT: アプリをパッケージ化して 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 の問題である可能性がある場合、どのような最適化がコードのこの部分に影響を与える可能性がありますか? 不安定なものを見つけるために 1 つずつオフにできるスイッチのリストはありますか?
アップデート:
私の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 ディレクトリ内の .
-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
-method でProGuard が行うことのコンパイル済み出力を見つけたと思います。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 :
私はこれにブレンドメソッドを書き直そうとしました(すべてのコンポーネントを同じように扱うと、もう1つだけを台無しにすることはできないはずです):
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 に対してのみ!?!)?