前言
之前翻看View源码,浏览的过程中就看到过很多十六进制的常量,以及位运算。
// View.class源码
static final int PFLAG3_VIEW_IS_ANIMATING_TRANSFORM = 0x1;
static final int PFLAG3_VIEW_IS_ANIMATING_ALPHA = 0x2;
static final int PFLAG3_IS_LAID_OUT = 0x4;
static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8;
static final int PFLAG3_CALLED_SUPER = 0x10;
static final int PFLAG3_APPLYING_INSETS = 0x20;
static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x40;
static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80;
static final int PFLAG3_SCROLL_INDICATOR_TOP = 0x0100;
...
// intent
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)
但始终没有思考为什么要这样写。恰巧近日在掘金上发现一篇就算不去火星种土豆,也请务必掌握的 Android 状态管理最佳实践!,讲的就是Android利用16进制进行多状态管理。
思考
Q: 遇到多状态的情况,原来我是怎样处理的?
A: 通过字符串的拼接;采用容器如数组,list或map;
Q: 16进制如何进行多状态管理,有什么样优势?
A: 整理如下!
16进制与2进制
理论基础
- 掌握16进制与2进制之间的转化,16进制每位对应4位2进制。
- 掌握位运算
- 与:
0x6 & 0x4 ==> 0110 & 0100 = 0100
- 或:
0x2 | 0x4 ==> 0010 | 0100 = 0110
- 取反:
~ 0x2 ==> ~ 0010 = 1101
- 与:
实战演练
/**
* 比如天气有多种状态:
* snow :雪
* rain :雨
*/
const val snow = 0x1 //0001
const val rain = 0x2 //0010
const val sun = 0x4 //0100
const val cloud = 0x8 //1000
...
//当前天气是雨夹雪,就可以表示为: snow | rain
val weather = snow or rain
// 0011 = 0001 | 0010
...
//过了一会儿,不下雨了: snow & ~rain
weather = weather and rain.inv()
// 0001 = 0011 & ~ 0010 = 0011 & 1101
...
//判断当前是不是下雨 (weather & rain)
val isSnow = weather and rain
// 0000 = 0001 & 0010
注意注释,可以更好的辅助理解
用法与优势
上述简单的举例,用法总结:
- 当需要
增加状态
采用按位或
- 当需要
移除状态
采用取反后,按位与
- 当需要
判断状态
采用按位与,判断结果是否为0,为0则不存在,反之存在
- 重中之重
相较于原来的字符串拼接或是集合的方式,优势尤为明显:
- 状态的增加或删除更容易,不需要字符串拆分或集合添加移除
- 判断状态更方便,不需要集合遍历
- 仅用int类型就可以完成状态的管理,对于存储更轻量
重中之重
16进制多状态管理本质上是二进制管理,即‘1’
所处的位数。
-
每种状态只能取单独位上为
‘1’
的二进制值,如下图所示 -
之所以要这样,可以通过设置一个其他值验证下,就会知道只能按上述定义取值。
const val snow = 0x3 //0011 const val rain = 0x2 //0010 ... //当前天气是雨夹雪,就可以表示为: snow | rain val weather = snow or rain // 0011 = 0011 | 0010 //这里可以看到,下雪状态设置成0x3,当状态增加时,或运算后,不足够表示雨夹雪状态。
-
既然本质是二进制,那么用其他进制如十进制应该也是可以的。但之所以选16进制,可能是更容易转换,更直观吧。