为什么源码中都使用16进制进行状态管理?

简介: 在Android源码中,对于“多状态”的管理总是通过16进制数字来表示,类似这种格式:

前言


在Android源码中,对于“多状态”的管理总是通过16进制数字来表示,类似这种格式:


//ViewGroup.java
protected int mGroupFlags;
static final int FLAG_CLIP_CHILDREN = 0x1;
private static final int FLAG_CLIP_TO_PADDING = 0x2;
static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
private static final int FLAG_RUN_ANIMATION = 0x8;
static final int FLAG_ANIMATION_DONE = 0x10;
private static final int FLAG_PADDING_NOT_NULL = 0x20;


那么,你有没有想过为什么遇到多状态的管理,就选择用16进制?


简单的状态表示


来举个实际的例子,我们作为一个人,身上肯定会有很多标签,比如帅气、可爱、博学、机智、懒惰、小气


针对这些标签,我们就可以设定不同的人设:


//定义实体类
 data class Person(var tag : String)
//修改标签
 val person1 = Person("帅气")
 //判断标签
  fun isCute():Boolean{
   return person1.tag == "可爱"
  }


当一个人只有一个标签的时候是很简单的,直接赋值或者取值判断即可。但是,如果一个人有多个标签呢?


也很简单,使用集合存储即可:


val person2 = Person(mutableListOf())
    person2.tags.add("帅气")
    person2.tags.add("可爱")
    person2.tags.remove("可爱")
    person2.tags.contains("可爱")


但是用到集合之后,这个计算就变得比较复杂了,由于removecontains方法都是通过遍历集合的方式实现的,从时间复杂度角度看的话,当删除某个标签或者判断某个标签是否存在的时间复杂度都是O(n)


有没有什么办法让多个标签也像刚才的单个标签那么简单地使用操作呢?


二进制运算


当然有啦,不然这篇文章也不会有了,在这之前,我们先复习下二进制的几种运算。


  • 1、按位与(&)


当两个对应位的值都为1,则结果为1,否则为0。


举例:0x1 & 0x4


0001 &
0100
     =
0000


  • 2、按位或(|)


当两个对应位的值都只要有一位是1,则结果为1。


举例:0x1 | 0x4


0001 |
0100
     =
0101


  • 3、取反( ~ )


将一个数按位取反。


举例:~ 0x1


0001 ~
     =
1110


好了,有了这三种运算,我们的状态管理就足够了。


引入16进制


接下来,就来完成一个完整的状态管理例子。


//设定所有状态对应的16进制值
//可爱,对应二进制0001
val TAG_CUTE = Ox1  
//帅气,对应二进制0010 
val TAG_HANDSOME = Ox2
//博学,对应二进制0100
val TAG_LEARNED = Ox4
var personTag = 0


状态增加


如果一个二进制数字想留下另一个二进制数字的痕迹,我们可以通过或运算,这样只要第二个数字某位上有1,那么最终的结果在同样的位数肯定也是1。


所以,我们可以通过这个方法来完成状态增加的功能:


//增加可爱状态
personTag |= TAG_CUTE
0000 |
0001 
=
0001


这样操作之后,personTag的第四位上的数字就为1了,也就带有TAG_CUTE这个标记了。


状态移除


按照上述的逻辑,状态的移除其实就是需要把对应的位数从1改为0。


假设personTag现在的值变成了二进制数0111


如果要删除TAG_CUTE属性,就需要把第四位的1改为0。那么我们可以做的操作就是先对TAG_CUTE取反,也就是把0001,变成了1110。然后再和personTag进行与运算,这样第四位肯定就会变为0,而其他位上面的值不变。


//personTag为二进制数0111
personTag &= ~TAG_CUTE
0001 ~
=
1110 &
0111
=
0110


完成对TAG_CUTE状态的移除。


状态判断


同理,对是否有某个状态的判断,其实就是判断在某个位上是否值为1。所以我们只需要对状态进行 与运算,如果结果为0,就代表没有这个状态,否则就代表有这个状态。


//personTag为二进制数0111
(personTag & TAG_CUTE) != 0
0111 &
0001
=
0001


结果不为0,所以代表personTag 包含了 TAG_CUTE 这个状态。


注意的点


细心的朋友可能会发现,刚才我们用到的16进制值,跳过了Ox3这个值,这是为什么呢?


其实不难发现,所谓的通过16进制管理状态,其实是通过二进制来管理状态,归根结底是通过二进制中的1所在的位数来进行管理。


所以我们对状态赋值,需要选取单独占有一位的二进制值,比如0001 ,0010,0100,1000,10000等等。


如果用了其他值会发生什么呢?举个例子,增加Ox3的TAG。


//懒惰,对应二进制0011
val TAG_LAZY = Ox3
//增加可爱状态
personTag |= TAG_CUTE
//增加帅气状态
personTag |= TAG_HANDSOME


在我们增加了可爱和帅气状态之后,personTag的二进制值为 0011


这时候再对它进行判断,是否含有懒惰状态:


//是否含有懒惰状态
(personTag & TAG_LAZY) != 0
0011 &
0011 
=
0011


结果不为0,难道我们增加了懒惰状态吗?很明显没有,我不懒但是却说我懒,这是诬陷!


所以你明白状态取值的范围了吗?


为什么是16进制?


到此,通过16进制管理状态的功能已经实现了,很明显这种方式管理状态要简便许多,其根本原理就是通过二进制的计算来完成对状态的管理。


有人又要问了,既然本质是通过二进制来完成管理,那么用10进制来表示也可以啊,比如上述的例子:


//设定所有状态对应的10进制值
//可爱,对应二进制0001
val TAG_CUTE = 1  
//帅气,对应二进制0010 
val TAG_HANDSOME = 2
//博学,对应二进制0100
val TAG_LEARNED = 4
var personTag = 0


这跟16进制不是一样么?


从根本来说,确实是一样的,但是16进制有16进制的好处,这就涉及到16进制为什么被设计出来的原因了。


在计算机中,一个字节有八位,最大值为 1111 1111。对应的10进制数是255,对应的16进制是 FF。所以半个字节用16进制是可以通过一个字母就能表示,而转换成10进制就是一个无规律的数字。为了方便,代码中一般使用16进制来表示 二进制,就是因为其可以和二进制进行一个更方便直观的转换。


总结


今天和大家介绍了下源码中常用的通过16进制转换2进制来管理状态的方法。


简单的、基础的道理解决大问题,这也许就是大道从简的含义?

目录
相关文章
|
SQL XML Java
mybatis Mapper的概念与实战
MyBatis 是一个流行的 Java 持久层框架,它提供了对象关系映射(ORM)的功能,使得Java对象和数据库中的表之间的映射变得简单。在MyBatis中,Mapper是一个核心的概念,它定义了映射到数据库操作的接口。简而言之,Mapper 是一个接口,MyBatis 通过这个接口与XML映射文件或者注解绑定,以实现对数据库的操作。
492 1
|
7月前
|
人工智能 自然语言处理 监控
LongPort MCP:证券业首个券商MCP,AI赋能智能投资新时代,散户也能玩转机构级交易
LongPort MCP是长桥集团推出的证券行业首个券商模型上下文协议,通过标准化接口实现AI与金融服务的无缝对接,支持自然语言交互的智能投资服务。
1054 8
LongPort MCP:证券业首个券商MCP,AI赋能智能投资新时代,散户也能玩转机构级交易
2024年 | 12月云大使推广奖励规则
【近期云大使规则升级】①上线企业云大使提现功能。②增加返利订单类目。③优化推广奖励限制。④提升首购后订单返利比例。⑤新增沉睡用户返利 。⑥推荐企业认证新用户首购最高奖励45%。
|
11月前
|
自然语言处理 数据可视化 BI
多部门协作难题有解!推荐几款实用的企业协作平台
在现代商业环境中,高效协作工具对于团队成功至关重要。本文推荐5款协作平台:板栗看板、Trello、Asana、Monday.com和ClickUp,它们分别在任务管理、实时沟通、数据安全等方面表现出色,帮助企业实现高效管理,提升项目成功率。选择合适的工具,可以显著提高团队效率和协作效果。
496 0
|
监控 Java 数据库
Java程序如何进行不停机更新?
Java程序如何进行不停机更新?
445 1
|
开发框架 前端开发 JavaScript
基于Admin.NET框架的前端的一些改进和代码生成处理(1)
基于Admin.NET框架的前端的一些改进和代码生成处理(1)
520专属——使用Python代码表白究竟能不能成功?
520,谐音:“我爱你”,一直觉得,520真正的意义,不单是用于表达爱,也不是为了收礼物和红包,而是提醒我们,不要忘记爱与被爱。 废话不多说,下面整理了9个效果图和参考代码,感兴趣的朋友可以看看
|
前端开发 JavaScript Go
除了 VueRouter,还有哪些常用的路由库?
除了 VueRouter,还有哪些常用的路由库?
319 5
|
前端开发
element-ui图标偶现乱码问题的原因和修复方法
之前很老的一个 webpack3 前端项目,用 vue-cli5 重构了一下,根据 vue-cli 文档安装的 sass 版本 ^1.32.7,sass-loader 版本 ^12.0.0,各种自测感觉没问题了就部署到线上了
|
SQL 关系型数据库 MySQL
服务器意外断电MySQL无法启动
服务器意外断电MySQL无法启动