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,那麼最終的結果在同樣的位數肯定也是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源碼使用16進制進行狀態管理的文章就介紹到這瞭,更多相關Android 16進制狀態管理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: