为什么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("可爱")
但是用到集合之后,这个计算就变得比较复杂了,由于remove和contains方法都是通过遍历集合的方式实现的,从时间复杂度角度看的话,当删除某个标签或者判断某个标签是否存在的时间复杂度都是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,那么最终的结果在同样的位数肯定也是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进制来管理状态的方法。
简单的、基础的道理解决大问题,这也许就是大道从简的含义?
拜拜
感谢大家的阅读,有一起学习的小伙伴可以关注下我的公众号——码上积木️️
每日一个知识点,积少成多,建立知识体系架构。
这里有一群很好的Android小伙伴,欢迎大家加入~
为什么Android源码中都使用16进制进行状态管理?的更多相关文章
- Android选择/拍照 剪裁 base64/16进制/byte上传图片+PHP接收图片
转载请注明出处:http://blog.csdn.net/iwanghang/article/details/65633129认为博文实用,请点赞,请评论,请关注.谢谢! ~ 老规矩,先上GIF动态图 ...
- Android中颜色透明度对应16进制值
透明度百分比对应的十六进制: (说明:百分比计算出来会有小数,按照常规的四舍五入处理,详情请往下查看) 百分比:0% HEX: 00 百分比:1% HEX: 30 百分比:2% HEX: 50 百分比 ...
- JS-011-颜色进制转换(RGB转16进制;16进制转RGB)
在网页开发的时候,经常需要进行颜色设置,因而经常需要遇到进行颜色进制转换的问题,例如:RGB转16进制:16进制转RGB),前几天在测试的时候,发现网站的颜色进制转换某类16进制颜色(例如:#0000 ...
- JavaScript中字符串与16进制之间的转换
一.字符串转换为16进制 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- Android源码中的FLAG为何使用16进制
1.在阅读源码的时候经常发现有一些标志属性使用一些位操作来判断是否具有该标志,增加标志或者去除标志. 比如View.java中的 /** * This view does not want keyst ...
- Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置
Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这 ...
- bgcolor RGB 和16进制之间的转换,16进制转RGB,源码
<p>bgcolor RGB 和16进制之间的转换,16进制转RGB,源码例如:<br /> 输入 201,255,201 转换成 #C9FFC9</p> < ...
- android.mk android源码编译
http://www.cnblogs.com/chenbin7/archive/2013/01/05/2846863.html Android.mk简单分析 2013-01-05 22:51 by . ...
- Android源码剖析之Framwork层后记篇(硬件消息传递、apk管理、输入法框架、编译过程)
本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 既然写到后记篇,就代表本系列到此为止,暂时告一段落:其他一些Manager随后有时间再补,就像源码的 ...
随机推荐
- 诸葛亮的锦囊妙计竟然是大名鼎鼎的Java设计模式:策略模式
目录 应用场景 简单实现例子 改进代码 策略模式 定义 意图 主要解决问题 何时使用 优缺点 诸葛亮的锦囊妙计 应用场景 京东.天猫双十一,情人节商品大促销,各种商品有不同的促销活动 满减:满200减 ...
- Day07_37_深度剖析集合中的contains()方法
深度剖析集合中的 contains()方法 contains()方法查找集合中是否包含某个元素 contains() 底层使用的是 equals()方法 当contains()方法拿到一个对象的时候, ...
- Word Reversal(string)
For each list of words, output a line with each word reversed without changing the order of the word ...
- Nginx篇
1 基本操作命令 先CD到nginx.exe目录 启动nginx服务 nginx start nginx 优雅停止nginx,有连接时会等连接请求完成再杀死worker进程 nginx -s quit ...
- Spring Boot的自动配置原理及启动流程源码分析
概述 Spring Boot 应用目前应该是 Java 中用得最多的框架了吧.其中 Spring Boot 最具特点之一就是自动配置,基于Spring Boot 的自动配置,我们可以很快集成某个模块, ...
- Pyqt5 combobox
起因 combobox的使用和介绍 两个combobox 联动 开始 介绍 Combobox是Qt中的下拉复选框, 注意:在添加列表选项时,可以一个个添加,也可以直接使用列表一次性添加多个: 添加多个 ...
- 关于width的继承和获取
absolute元素(如果没有设置width值),其宽度自适应于内部元素, <!DOCTYPE html> <html lang="en"> <hea ...
- SpringCloud之远程调用OpenFeign和Ribbon
Ribbon.Feign和OpenFeign的区别 SpringCloudAlibaba微服务实战教程系列 Spring Cloud 微服务架构学习记录与示例 一 简介 Feign是Netflflix ...
- hdu4995 (不错的小模拟)
题意: 输入n,m,k ,给你n个点,他们在一个一维坐标上,每个点有两个值,一个是坐标,另一个是价值,然后有m组操作,每次操作给一个坐标,意思就是把当前这个坐标的点距离他最近的k个点(相等 ...
- Java安全之FastJson JdbcRowSetImpl 链分析
Java安全之FastJson JdbcRowSetImpl 链分析 0x00 前言 续上文的Fastjson TemplatesImpl链分析,接着来学习JdbcRowSetImpl 利用链,Jdb ...
