这篇文章主要讲解了“源码中都使用16进制进行状态管理的原因”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“源码中都使用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进制值 //可爱,对应二进制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进制管理状态的功能已经实现了,很明显这种方式管理状态要简便许多,其根本原理就是通过二进制的计算来完成对状态的管理。
有人又要问了,既然本质是通过二进制来完成管理,那么用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进制进行状态管理的原因”的内容了,经过本文的学习后,相信大家对源码中都使用16进制进行状态管理的原因这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。