直到 2017 年的夏天,我在和一位彼时 3 年经验的同事,联手完成当年扛鼎项目的核心功能时,因同事提出使用十六进制管理状态,而亲眼见证了十六进制在状态管理方面的绝佳优势。
为了纪念同事的这一分享,此后每当有新同事入职,我提供的培训课程必包含十六进制状态管理。
使用十六进制前的混沌世界
该项目有个需求:当指定图形编辑的模式时,图形工具栏的按钮状态要随之发生配套性地变化。
例如,存在 3 种图形编辑模式,和 8 个图形编辑按钮。
模式 A 下,要求 按钮1、按钮2、按钮3 可用,其他按钮禁用。
模式 B 下,要求 按钮1、按钮4、按钮5、按钮6 可用,其他按钮禁用。
模式 C 下,要求 按钮1、按钮7、按钮8 可用,其他按钮禁用。
如果是传统方式编写,我们势必会在类中为 3 个模式定义 boolean 变量,为 8 个按钮状态定义 boolean 变量。
那么在模式切换时,就需要将每个按钮状态的变量都 “清洗” 一遍。例如:
public void setModeA() {
status1 = true;
status2 = true;
status3 = true;
status4 = false;
status5 = false;
status6 = false;
status7 = false;
status8 = false;
}
public void setModeB() {
status1 = true;
status2 = false;
status3 = false;
status4 = true;
status5 = true;
status6 = true;
status7 = false;
status8 = false;
}
public void setModeC() {
…
}
那要是日后模式变多、按钮状态变多,类中就会满是这种 setMode 的方法,看起来很蠢,而且密密麻麻的 true、false,极容易出错。
这是一点。
另一点就是,如果按钮状态是用 boolean 变量来管理,那么状态的存储和读取怎么办呢?
- 每个 boolean 变量都要转换成 int 类型的 0 或 1 存储在数据库中。
- 数据库需要为每个状态准备一个字段。
- 读取的时候又要负责将每个状态转译回 boolean。
这工作量也太大了!而且日后每添加或修改一个状态,数据库都要新增或修改字段,这非常低效和不安全!
十六进制能很好地解决这些问题
十六进制可以做到:
- 通过状态集的注入,一行代码即可完成模式的切换。
- 无论再多的状态,都只需要一个字段来存储。状态被存放在 int 类型的状态集中,可以直接向数据库写入或读取。
十六进制的运作机制
在具体了解十六进制是怎么做到状态管理最佳实践之前,我们先简单过一遍十六进制本身的运作机制。
首先,在编程中,利用开头 0x 表示十六进制数。
例如 0x0001,0x0002。
然后,十六进制的计算,我们可以借助二进制的 “按位计算” 方式来理解。
二进制存在 与、或、异或、取反 等操作:
a & b,a | b,a ^ b,~a
例如,十六进制数 0x0004 | 0x0008,可以理解为:
0100
|
1000
1100
十六进制 (0x0004 | 0x0008) & 0x0004 可以得到:
1100
&
0100
0100
也即状态集中包含某状态时,再与上该状态,就会得到非 0 的结果。
于是,我们就可以利用这个特性来完成状态管理:
十六进制的状态管理实战
- 首先我们定义一个状态集变量,用来存放当前模式的状态集,例如:
private int STATUSES;
- 然后我们定义十六进制状态常量,和模式状态集,例如:
private final int STATUS_1 = 0x0001;
private final int STATUS_2 = 0x0002;
private final int STATUS_3 = 0x0004;
private final int STATUS_4 = 0x0008;
private final int STATUS_5 = 0x0010;
private final int STATUS_6 = 0x0020;
private final int STATUS_7 = 0x0040;
private final int STATUS_8 = 0x0080;
private final int MODE_A = STATUS_1 | STATUS_2 | STATUS_3;
private final int MODE_B = STATUS_1 | STATUS_4 | STATUS_5 | STATUS_6;
private final int MODE_C = STATUS_1 | STATUS_7 | STATUS_8;
- 当我们需要往状态集中添加状态时,就通过或运算。例如:
STATUSES | STATUS_1
- 当我们需要从状态集中移除状态时,就通过取反运算。例如:
STATUSES & ~ STATUS_1
- 当我们需要判断状态集中是否包含某状态时,就通过与运算。结果为 0 即代表无,反之有。
public static boolean isStatusEnabled(int statuses, int status) {
return (statuses & status) != 0;
}
- 当我们需要切换模式时,我们可以直接将预先定义好的 “模式状态集” 赋予给状态集变量。例如:
STATUSES = MODE_A;
如此,复杂度从 m * n 骤减为 m + n,随着日后模式和状态的增多,十六进制的优势将指数级增长!
是不是超简洁?再也不需要定义和修改各种 “setModeXXX” 方法了。
而且这还只是一半。另一半是关于十六进制状态的存取。
十六进制的状态存取实战
由于状态集是 int 类型,因而我们最少只需一个字段,即可存储状态集:
insert into tableXXX TITLE,DATE,STATUS values (‘xxx’,‘20190703’,32)
读取也十分简单,读取后直接赋值给 STATUSES 即可。
除此之外,你还可以直接在 SQL 中通过按位计算来查询!例如查询包含状态 0x0004 的记录:
select * from tableXXX where STATUS & 4 != 0
综上
在没有十六进制的日子里,状态管理是个繁琐的、极易出错的操作。
有了十六进制后:
结语
- 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
- 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
- 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
- OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
所以提前做了一些准备。现在拿出来分享给大家。
[外链图片转存中…(img-s5wLNYpY-1715258723027)]
[外链图片转存中…(img-LXLMSnqd-1715258723027)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!