有时候,当我们需要记录下多种状态,并且每一种都是可以同时存在的,
他们是并列关系,每种状态可以有两个结果,是否(true|false)。在很多框架中,会使用到下面这种方法。
下面先来看一段代码,这段代码来自Glide源代码
private static final int UNSET = -1;
private static final int SIZE_MULTIPLIER = 1 << 1;
private static final int DISK_CACHE_STRATEGY = 1 << 2;
private static final int PRIORITY = 1 << 3;
private static final int ERROR_PLACEHOLDER = 1 << 4;
private static final int ERROR_ID = 1 << 5;
private static final int PLACEHOLDER = 1 << 6;
private static final int PLACEHOLDER_ID = 1 << 7;
private static final int IS_CACHEABLE = 1 << 8;
private static final int OVERRIDE = 1 << 9;
private static final int SIGNATURE = 1 << 10;
private static final int TRANSFORMATION = 1 << 11;
private static final int RESOURCE_CLASS = 1 << 12;
private static final int FALLBACK = 1 << 13;
private static final int FALLBACK_ID = 1 << 14;
private static final int THEME = 1 << 15;
private static final int TRANSFORMATION_ALLOWED = 1 << 16;
private static final int TRANSFORMATION_REQUIRED = 1 << 17;
private static final int USE_UNLIMITED_SOURCE_GENERATORS_POOL = 1 << 18;
private static final int ONLY_RETRIEVE_FROM_CACHE = 1 << 19;
private static final int USE_ANIMATION_POOL = 1 << 20;
1 << 1
是一个位运算表达式,其中 <<
是左移操作符。这个表达式的意思是将数字 1
在二进制表示中的位向左移动 1
位。以此类推,到 1 << 20
最后一个向左移动20位。每一个状态都是用一个整数存储。当我们转换成整数表示之后。
private static final int UNSET = -1; // 在Java中,-1的二进制表示是所有位都为1(32位),但在10进制中它就是-1
private static final int SIZE_MULTIPLIER = 2; // 1 << 1
private static final int DISK_CACHE_STRATEGY = 4; // 1 << 2
private static final int PRIORITY = 8; // 1 << 3
private static final int ERROR_PLACEHOLDER = 16; // 1 << 4
private static final int ERROR_ID = 32; // 1 << 5
private static final int PLACEHOLDER = 64; // 1 << 6
private static final int PLACEHOLDER_ID = 128; // 1 << 7
private static final int IS_CACHEABLE = 256; // 1 << 8
private static final int OVERRIDE = 512; // 1 << 9
private static final int SIGNATURE = 1024; // 1 << 10
private static final int TRANSFORMATION = 2048; // 1 << 11
private static final int RESOURCE_CLASS = 4096; // 1 << 12
private static final int FALLBACK = 8192; // 1 << 13
private static final int FALLBACK_ID = 16384; // 1 << 14
private static final int THEME = 32768; // 1 << 15
private static final int TRANSFORMATION_ALLOWED = 65536; // 1 << 16
private static final int TRANSFORMATION_REQUIRED = 131072; // 1 << 17
private static final int USE_UNLIMITED_SOURCE_GENERATORS_POOL = 262144; // 1 << 18
private static final int ONLY_RETRIEVE_FROM_CACHE = 524288; // 1 << 19
private static final int USE_ANIMATION_POOL = 1048576; // 1 << 20
- 二进制表示
简单来说,左移 n
位相当于乘以 2
的 n
次方。所以 1 << 1
相当于 1 * 2^1 = 2
,当我们继续把它转成二进制
rivate static final int UNSET = -1; // 在32位整数中,这是 11111111 11111111 11111111 11111111(二进制)
private static final int SIZE_MULTIPLIER = 0000 0000 0000 0000 0000 0000 0000 0010; // 1 << 1
private static final int DISK_CACHE_STRATEGY = 0000 0000 0000 0000 0000 0000 0000 0100; // 1 << 2
private static final int PRIORITY = 0000 0000 0000 0000 0000 0000 0000 1000; // 1 << 3
private static final int ERROR_PLACEHOLDER = 0000 0000 0000 0000 0000 0000 0001 0000; // 1 << 4
private static final int ERROR_ID = 0000 0000 0000 0000 0000 0000 0010 0000; // 1 << 5
private static final int PLACEHOLDER = 0000 0000 0000 0000 0000 0000 0100 0000; // 1 << 6
private static final int PLACEHOLDER_ID = 0000 0000 0000 0000 0000 0000 1000 0000; // 1 << 7
private static final int IS_CACHEABLE = 0000 0000 0000 0000 0000 0001 0000 0000; // 1 << 8
private static final int OVERRIDE = 0000 0000 0000 0000 0000 0010 0000 0000; // 1 << 9
private static final int SIGNATURE = 0000 0000 0000 0000 0000 0100 0000 0000; // 1 << 10
private static final int TRANSFORMATION = 0000 0000 0000 0000 0000 1000 0000 0000; // 1 << 11
private static final int RESOURCE_CLASS = 0000 0000 0000 0000 0001 0000 0000 0000; // 1 << 12
private static final int FALLBACK = 0000 0000 0000 0000 0010 0000 0000 0000; // 1 << 13
private static final int FALLBACK_ID = 0000 0000 0000 0000 0100 0000 0000 0000; // 1 << 14
private static final int THEME = 0000 0000 0000 0000 1000 0000 0000 0000; // 1 << 15
private static final int TRANSFORMATION_ALLOWED = 0000 0000 0000 0000 0001 0000 0000 0000; // 1 << 16
private static final int TRANSFORMATION_REQUIRED = 0000 0000 0000 0010 0000 0000 0000 0000; // 1 << 17
private static final int USE_UNLIMITED_SOURCE_GEN= 0000 0000 0000 0100 0000 0000 0000 0000; // 1 << 18
private static final int ONLY_RETRIEVE_FROM_CACHE =0000 0000 0000 1000 0000 0000 0000 0000; // 1 << 19
private static final int USE_ANIMATION_POOL = 0000 0000 0001 0000 0000 0000 0000 0000; // 1 << 20
当一个int,用二进制去表示的时候,它可以有32位。所以每一位都可以用来存在一种状态。当把每种状态都存满之后,就是以上状态合并。0000 0000 0001 1111 1111 1111 1111 1110
- 存储数据
那平时怎么去存储,或者清除掉呢,比如我需要把SIZE_MULTIPLIER
记录下来,或者把SIZE_MULTIPLIER
清除掉,记录下来就是把对应位置设置成1
,删除掉就是把对应位置还原回到0
。
fields = fields | SIZE_MULTIPLIER; 等同 fields |= SIZE_MULTIPLIER;
通过|
运算符就是存储,简单理解就是当两个数组合的时候,如果有一个是1,那对应位置就是1.
假如当前fields值是
0000 0000 0001 1111 1111 1111 1111 1110 (fields)| 0000 0000 0000 0000 0000 0000 0000 0010 (SIZE_MULTIPLIER)
他们最后结果就是0000 0000 0001 1111 1111 1111 1111 1110
也就是被记录下来了,这种算法,它只关注对应位置,其他位置并不影响,这里对应位置应该就是第二位了。
- 移除数据
fields = fields & ~PRIORITY;
等同 fields &= ~PRIORITY;
假如原来的值是fields = 0000 0000 0001 1111 1111 1111 1111 1110
某个状态 PRIORITY = 0000 0000 0000 0000 0000 0000 0000 1000
取反~PRIORITY = 1111 1111 1111 1111 1111 1111 1111 0111
fields = fields & ~PRIORITY
0000 0000 0001 1111 1111 1111 1111 1110 (fields) & 1111 1111 1111 1111 1111 1111 1111 0111 (~PRIORITY)
=
0000 0000 0001 1111 1111 1111 1111 0110
&
运算规则是两个都是1
的时候,才是1
,所以上面计算结果为
0000 0000 0001 1111 1111 1111 1111 0110
其实变化就是第四位变成0
了
- 提取数据 先来看一下关于View可见性的状态
public static final int VISIBLE = 0x00000000;
public static final int INVISIBLE = 0x00000004;
public static final int GONE = 0x00000008;
对应二进制,省略掉前面是0的位
public static final int VISIBLE = 0000;
public static final int INVISIBLE = 0100;
public static final int GONE = 1000;
观察数据可以发现,它是存储在mViewFlags
后面位,那么怎么把View状态提取出来?
public int getVisibility() {
return mViewFlags & VISIBILITY_MASK;
}
通过VISIBILITY_MASK & mViewFlags
static final int VISIBILITY_MASK = 0x0000000C;
对应二进制,省略掉前面是0的位
static final int VISIBILITY_MASK = 1100;
也就是取一个对应位置都是1
的MASK
去&
运算就可以提取状态了。
- 组合两个数,形成一个唯一的
Key
把两个参数结合一起,作为判断条件,比如属性A
,属性B
,两个条件一起满足的时候,才执行一些逻辑。在一些缓存场景中,需要用到。
下面看一个二进制
00000000000000000000000000001111 00000000000000000000000000001111
这个二进制有64
位,前面32
位假如存储A
,后面32
位存储B
,通过这样的组合生成一个key
,总共就是64位,在Java
中可以用一个long
表示。下面继续看一下实际的源代码使用。
final boolean isColorDrawable;
final DrawableCache caches;
final long key;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable = true;
caches = mColorDrawableCache;
key = value.data;
} else {
isColorDrawable = false;
caches = mDrawableCache;
key = (((long) value.assetCookie) << 32) | value.data;
}
key = (((long) value.assetCookie) << 32) | value.data
这段代码就是把assetCookie
存储在一个long
的前面32
位,把data
存储在后面32
位。比如
假如assetCookie=9
,那它的二进制就是1001
,转成long
之后,64位就是前面一堆
0000000000000000000000000000000000000000000000000000000000001001
然后左移动32位
0000000000000000000000000000100100000000000000000000000000000000
假如data=2264
,那它的二进制就是100011011000
。
当把assetCookie << 32|data
,也就是0000000000000000000000000000100100000000000000000000000000000000
assetCookie << 32
0000000000000000000000000000000000000000000000000000100011011000
data
两个重叠之后
0000000000000000000000000000100100000000000000000000100011011000
- 总结
三种运算符使用 & | ~
如何通过一个Int
整数表示32
种状态