Android的消息機(jī)制主要是指Handler的運行機(jī)制,Handler的運行需要底層的MessageQueue與Looper的支撐。Handler的主要任務(wù)是將一個任務(wù)切換到某個指定的線程中去執(zhí)行。
Messageueue的中文翻譯消息隊列,顧名思義,它的內(nèi)部存儲了一組信息,一隊列的形式對外提供插入和刪除的工作。雖然叫做消息隊列,但是它的內(nèi)部存儲結(jié)婚并不是真正的隊列,而是采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息列表。
Looper的中文翻譯為循環(huán),而在這里可以理解為消息循環(huán)。Looper會以無限循環(huán)的形式去查找是否有新消息,如果有的話就處理消息,否則就一直等待。Looper中還有一個特殊的概念,就是ThreadLocal,ThreadLocal并不是線程。它的作用是可以在每個線程中存儲數(shù)據(jù)。ThreadLocal可以在不同線程中互不干擾的存儲并提供數(shù)據(jù),通過ThreadLocal可以輕松獲取每個線程的Looper。當(dāng)然,線程是沒有Looper的,如果需要使用Handler就必須為線程創(chuàng)建Looper。
我們經(jīng)常提到的主線程,也叫UI線程,它就是ActivityThread,它被創(chuàng)建時就會初始化Looper,這也是主線程中默認(rèn)可以使用Handler的原因。
提供Handler原因:
因為Android規(guī)定訪問UI只能在主線程中進(jìn)行,如果子線程中訪問UI,那么程序就會拋出異常。但是Android又建議不要在主線程中執(zhí)行耗時操作,否則會導(dǎo)致程序無法響應(yīng)ANR。因此handler就是為了解決子線程中無法訪問UI的矛盾。
而系統(tǒng)又為啥不允許在子線程中訪問UI?
這是因為Android的UI控件不是線程安全的,如果在多線程中并發(fā)訪問可能會導(dǎo)致UI控件處于不可預(yù)期的狀態(tài),那為什么系統(tǒng)不對UI控件的訪問加上鎖機(jī)制呢?缺點有兩個:首先加上鎖機(jī)制會讓UI訪問的邏輯變得復(fù)雜;其次鎖機(jī)制會降低UI訪問的效率,因為鎖機(jī)制會阻塞某些線程的執(zhí)行。
ThreadLocal
工作原理
ThreadLocal是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程中存儲數(shù)據(jù),數(shù)據(jù)存儲以后,只有在指定的線程中可以獲取到存儲的數(shù)據(jù),對于其他線程中來說則無法獲取到數(shù)據(jù)。
使用場景
當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時候,就可以考慮采用ThreadLocal。
ThreadLocal的另一個使用場景是復(fù)雜邏輯下的對象傳遞。
例子:
privateThreadLocalmBooleanThreadLocal=newThreadLocal();

得到:

其中,ThreadLocal的set方法:

線程隔離的秘密,就在于ThreadLocalMap這個類。ThreadLocalMap是ThreadLocal類的一個靜態(tài)內(nèi)部類,它實現(xiàn)了鍵值對的設(shè)置和獲?。▽Ρ萂ap對象來理解),每個線程中都有一個獨立的ThreadLocalMap副本,它所存儲的值,只能被當(dāng)前線程讀取和修改。ThreadLocal類通過操作每一個線程特有的ThreadLocalMap副本,從而實現(xiàn)了變量訪問在不同線程中的隔離。因為每個線程的變量都是自己特有的,完全不會有并發(fā)錯誤。還有一點就是,ThreadLocalMap存儲的鍵值對中的鍵是this對象指向的ThreadLocal對象,而值就是你所設(shè)置的對象了。
對于getMap和createMap方法的源碼如下:

接下來看其get方法:

其中可看setInitialValue方法:

獲取和當(dāng)前線程綁定的值時,ThreadLocalMap對象是以this指向的ThreadLocal對象為鍵進(jìn)行查找的,這當(dāng)然和前面set()方法的代碼是相呼應(yīng)的。
進(jìn)一步地,我們可以創(chuàng)建不同的ThreadLocal實例來實現(xiàn)多個變量在不同線程間的訪問隔離,為什么可以這么做?因為不同的ThreadLocal對象作為不同鍵,當(dāng)然也可以在線程的ThreadLocalMap對象中設(shè)置不同的值了。通過ThreadLocal對象,在多線程中共享一個值和多個值的區(qū)別,就像你在一個HashMap對象中存儲一個鍵值對和多個鍵值對一樣,僅此而已。