直到 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
综上
在没有十六进制的日子里,状态管理是个繁琐的、极易出错的操作。
有了十六进制后:
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门**
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!