一逼争、Handler
1茴厉、消息機(jī)制是什么姓言?Handler是什么邓萨?
1)在Android中,消息機(jī)制主要就是指Handler機(jī)制锨天。
2)Handler是Android中的消息機(jī)制毯盈。它可以發(fā)送和處理延時(shí)消息/任務(wù),并且可以把消息/任務(wù)發(fā)送到其他線程(線程切換)病袄。實(shí)際開發(fā)中搂赋,主要是用來解決在子線程中無(wú)法更新UI的問題。
2益缠、為什么不能在子線程中訪問UI脑奠?
ViewRootImpl會(huì)對(duì)UI操作進(jìn)行驗(yàn)證,禁止在子線程中訪問UI:
void checkThread(){
if(mThread != Thread.currentThread()){
throw new CalledFromWrongThreadException("Only th original thread that created a view hierarchy can touch its views");
}
}
3幅慌、Android為什么要把UI更新限制在主線程宋欺,以及Handler的由來?
最根本的目的在于解決:多線程并發(fā)問題胰伍。
分析:如果在一個(gè)Activity中有多個(gè)線程齿诞,并且沒有加鎖,那么同時(shí)去操作UI時(shí)就出現(xiàn)界面錯(cuò)亂的問題骂租。但是如果對(duì)這些更新UI的操作都做加鎖處理祷杈,又會(huì)導(dǎo)致性能下降。出于對(duì)性能問題的考慮渗饮,Android提供了一套更新UI的機(jī)制——Handler但汞。我們只需要遵循這種機(jī)制,就可以方便的進(jìn)行UI操作互站,而不用再去關(guān)心多線程的問題私蕾,所有更新UI的操作,都是在主線程的消息隊(duì)列中去輪訓(xùn)的云茸。
4是目、在子線程中創(chuàng)建Handler報(bào)錯(cuò)是為什么?
子線程默認(rèn)是沒有Looper的谤饭,進(jìn)而也就沒有MessageQueue标捺。因此要在子線程中創(chuàng)建Handler,那么首先得在子線程中調(diào)用Looper.prepare()揉抵。
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
Looper.prepare()后亡容,當(dāng)前的線程就會(huì)和一個(gè)Looper關(guān)聯(lián),進(jìn)而和MessageQueue關(guān)聯(lián)冤今。然后又因?yàn)镠andler在創(chuàng)建時(shí)闺兢,會(huì)和Looper關(guān)聯(lián),所有到此Handler、Looper屋谭、MessageQueue脚囊、Thread之間一一對(duì)應(yīng)的關(guān)系也就建立了。
到這個(gè)階段桐磁,我們就可以使用Handler發(fā)送消息了悔耘。但是,現(xiàn)在只是可以把消息加入到MessageQueue中我擂,想要當(dāng)前的子線程會(huì)去取出消息衬以,還需要執(zhí)行Looper.loop()。
小結(jié):要在子線程中new Handler就分為三步:
1)Looper.prepare() --負(fù)責(zé)為Thread準(zhǔn)備MessageQueue校摩。
2)Looper.loop() ------負(fù)責(zé)從MessageQueue中取消息看峻。
3)new Handler()。 ---負(fù)責(zé)消息發(fā)送與處理衙吩。
其實(shí)在主線程也是這樣操作的互妓,只不過系統(tǒng)已經(jīng)幫我們做了前兩步了。
5坤塞、為什么通過Handler能實(shí)現(xiàn)線程的切換车猬?
Handler創(chuàng)建的時(shí)候會(huì)和Looper進(jìn)行關(guān)聯(lián),進(jìn)而關(guān)聯(lián)了MessageQueue和Thread尺锚,并且一個(gè)Handler對(duì)應(yīng)的Looper珠闰、MessageQueue和Thread是唯一的。因此瘫辩,不管Handler在什么地方使用(其他線程)伏嗜,它發(fā)送的消息都是加入到自己對(duì)應(yīng)的線程/MessageQueue中了,然后取消息也是在它對(duì)應(yīng)的線程伐厌,因此也就實(shí)現(xiàn)了跨線程承绸。
6、Handler.post的邏輯在哪個(gè)線程執(zhí)行的挣轨,是由Looper所在線程還是Handler所在線程決定的军熏?
這個(gè)問題,應(yīng)該可以分為兩步:
1)post方法本身是在調(diào)用post方法的線程中執(zhí)行的卷扮,一直執(zhí)行到把消息/Runnable加入到MessageQueue中
2)post出去的Runnable的run()方法荡澎,是在Looper所在的線程執(zhí)行的。在這個(gè)線程中Looper.loop()會(huì)一直取消息晤锹,取出后摩幔,調(diào)用當(dāng)前線程對(duì)應(yīng)的Handler.dispatchMessage執(zhí)行。
7鞭铆、Looper和Handler一定要處于一個(gè)線程嗎或衡?子線程中可以用MainLooper去創(chuàng)建Handler嗎?
Handler在任何線程創(chuàng)建都可以,它創(chuàng)建的時(shí)候默認(rèn)是和當(dāng)前線程的Looper關(guān)聯(lián)封断,但也可以指定Looper斯辰。
比如,在子線程中坡疼,可以創(chuàng)建一個(gè)Handler椒涯,然后指定Looper為MainLooper。那么此時(shí)這個(gè)Handler所發(fā)出的消息回梧,都是在主線程收到的废岂。
8、Handler的post/send()的原理
一系列post/send方法最終都是通過enqueueMessage()方法將msg加入到MessageQueue中狱意。
9湖苞、Handler的post方法發(fā)送的是同步消息嗎?可以發(fā)送異步消息嗎详囤?
1)用戶層面發(fā)送的都是同步消息财骨,不能發(fā)異步消息
2)異步消息只能系統(tǒng)發(fā)送。
疑問:
10、Handler的post()和postDelayed()方法的異同羔杨?
1)底層都是調(diào)用的sendMessageDelayed()
2)post()傳入的時(shí)間參數(shù)為0
3)postDelayed()傳入的時(shí)間參數(shù)是需要延遲的時(shí)間間隔捌臊。
11、Handler的postDelayed的底層機(jī)制
postDelayed --> sendMessageDelayed --> sendMessageAtTime --> enqueueMessage兜材。
12理澎、Handler的dispatchMessage()分發(fā)消息的處理流程?
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
13曙寡、Handler為什么要有Callback的構(gòu)造方法糠爬?
不需要派生Handler。
14举庶、Handler構(gòu)造方法中通過Looper.myLooper();是如何獲取到當(dāng)前線程的Looper的执隧?
myLooper()內(nèi)部使用ThreadLocal實(shí)現(xiàn),因此能夠獲取各個(gè)線程自己的Looper户侥。
15镀琉、主線程如何向子線程發(fā)送消息?
1)通過在主線程調(diào)用子線程中Handler的post方法添祸,完成消息的投遞滚粟。
2)通過HandlerThread實(shí)現(xiàn)該需求寻仗。
16、MessageQueue.next()會(huì)因?yàn)榘l(fā)現(xiàn)了延遲消息,而進(jìn)行阻塞耙替。那么為什么后面加入的非延遲消息沒有被阻塞呢亚侠?
二、MessageQueue
1俗扇、MessageQueue是什么硝烂?
1)消息隊(duì)列
2)內(nèi)部存儲(chǔ)結(jié)構(gòu)并不是真正的隊(duì)列,而是用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)消息列表
3)只能存儲(chǔ)消息铜幽,不能處理消息
2滞谢、MessageQueue的主要兩個(gè)操作是什么?有什么用?
1)enqueueMessage:往消息隊(duì)列中插入一條消息
2)next:取出一條消息除抛,并從消息隊(duì)列中移除
3)本質(zhì)采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息隊(duì)列狮杨,而不是采用隊(duì)列
3、MessageQueue的enqueueMessage()方法的原理到忽,如何進(jìn)行線程同步的橄教?
boolean enqueueMessage(Message msg, long when) {
//1. 內(nèi)部是單鏈表的插入操作
synchronized (this) {
......
}
return true;
}
1)單鏈表的插入操作
2)如果消息隊(duì)列被阻塞回調(diào)用nativeWake去喚醒。
3)用synchronized代碼塊去進(jìn)行同步喘漏。
4护蝶、MessageQueue的next()方法內(nèi)部的原理?
/**
* 功能:讀取并且刪除數(shù)據(jù)
* 內(nèi)部無(wú)限循環(huán)翩迈,如果消息隊(duì)列中沒有消息就會(huì)一直阻塞持灰。
* 一旦有新消息到來,next方法就會(huì)返回該消息并且將其從單鏈表中移除
*/
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
/**======================================================================
* 1负饲、精確阻塞指定時(shí)間搅方。第一次進(jìn)入時(shí)因?yàn)閚extPollTimeoutMillis=0,因此不會(huì)阻塞绽族。
* 1-如果nextPollTimeoutMillis=-1姨涡,一直阻塞不會(huì)超時(shí)。
* 2-如果nextPollTimeoutMillis=0吧慢,不會(huì)阻塞涛漂,立即返回。
* 3-如果nextPollTimeoutMillis>0检诗,最長(zhǎng)阻塞nextPollTimeoutMillis毫秒(超時(shí))匈仗,如果期間有程序喚醒會(huì)立即返回。
*====================================================================*/
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 當(dāng)前時(shí)間
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
/**=======================================================================
* 2逢慌、當(dāng)前Msg為消息屏障
* 1-說明有重要的異步消息需要優(yōu)先處理
* 2-遍歷查找到異步消息并且返回悠轩。
* 3-如果沒查詢到異步消息,會(huì)continue攻泼,且阻塞在nativePollOnce直到有新消息
*====================================================================*/
if (msg != null && msg.target == null) {
// 遍歷尋找到異步消息火架,或者末尾都沒找到異步消息鉴象。
do {
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
/**================================================================
* 3、獲取到消息
* 1-消息時(shí)間已到何鸡,返回該消息纺弊。
* 2-消息時(shí)間沒到,表明有個(gè)延時(shí)消息骡男,會(huì)修正nextPollTimeoutMillis淆游。
* 3-后面continue,精確阻塞在nativePollOnce方法
*===================================================================*/
if (msg != null) {
// 延遲消息的時(shí)間還沒到隔盛,因此重新計(jì)算nativePollOnce需要阻塞的時(shí)間
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 返回獲取到的消息(可以為一般消息犹菱、時(shí)間到的延遲消息、異步消息)
return msg;
}
} else {
/**=============================
* 4吮炕、沒有找到消息或者異步消息
*==============================*/
nextPollTimeoutMillis = -1;
}
/**===========================================
* 5已亥、沒有獲取到消息,進(jìn)行下一次循環(huán)来屠。
* (1)此時(shí)可能處于的情況:
* 1-沒有獲取到消息-nextPollTimeoutMillis = -1
* 2-沒有獲取到異步消息(接收到同步屏障卻沒找到異步消息)-nextPollTimeoutMillis = -1
* 3-延時(shí)消息的時(shí)間沒到-nextPollTimeoutMillis = msg.when-now
* (2)根據(jù)nextPollTimeoutMillis的數(shù)值虑椎,最終都會(huì)阻塞在nativePollOnce(-1),
* 直到enqueueMessage將消息添加到隊(duì)列中俱笛。
*===========================================*/
if (pendingIdleHandlerCount <= 0) {
// 用于enqueueMessage進(jìn)行精準(zhǔn)喚醒
mBlocked = true;
continue;
}
}
}
}
小結(jié)(原理:分為三種情況進(jìn)行處理):
1)如果是一般消息捆姜,會(huì)去獲取消息,沒有獲取到就會(huì)阻塞(native方法)迎膜,直到enqueueMessage插入新消息泥技。獲取到直接返回Msg。
2)如果是同步屏障磕仅,會(huì)去循環(huán)查找異步消息珊豹,沒有獲取到會(huì)進(jìn)行阻塞。獲取到直接返回Msg榕订。
3)如果是延時(shí)消息店茶,會(huì)計(jì)算時(shí)間間隔,并進(jìn)行精準(zhǔn)定時(shí)阻塞(native方法)劫恒。直到時(shí)間到達(dá)或者被enqueueMessage插入消息而喚醒贩幻。時(shí)間到后就返回Msg。
疑問:
5两嘴、Looper.loop()是如何阻塞的丛楚?MessageQueue.next()是如何阻塞的?
通過native方法:nativePollOnce()進(jìn)行精準(zhǔn)時(shí)間的阻塞憔辫。
三趣些、Looper
1、Looper是什么贰您?
1坏平、輪詢器拢操。(消息循環(huán))
2、Looper以無(wú)限循環(huán)的形式去查找是否有新消息功茴,有就處理消息庐冯,沒有就一直等待著孽亲。
2坎穿、如何開啟消息循環(huán)?
Looper.loop()返劲。
3玲昧、Looper的構(gòu)造方法
private Looper(boolean quitAllowed) {
//1. 會(huì)創(chuàng)建消息隊(duì)列: MessageQueue
mQueue = new MessageQueue(quitAllowed);
//2. 當(dāng)前線程
mThread = Thread.currentThread();
}
4、為線程創(chuàng)建Looper
//1. 在沒有Looper的線程創(chuàng)建Handler會(huì)直接異常
new Thread("Thread#2"){
@Override
public void run(){
Handler handler = new Handler();
}
}.start();
異常:
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
//2. 用prepare為當(dāng)前線程創(chuàng)建一個(gè)Looper
new Thread("Thread#2"){
@Override
public void run(){
Looper.prepare();
Handler handler = new Handler();
//3. 開啟消息循環(huán)
Looper.loop();
}
}.start();
5篮绿、主線程ActivityThread中的Looper的創(chuàng)建和獲取
1)主線程中使用prepareMainLooper()創(chuàng)建Looper
2)getMainLooper能夠在任何地方獲取到主線程的Looper
6孵延、Looper的兩個(gè)退出方法?有什么區(qū)別亲配?
1)Looper的退出有兩個(gè)方法:quit和quitSafely
2)quit會(huì)直接退出Looper
3)quitSafely只會(huì)設(shè)置退出標(biāo)記尘应,在已有消息全部處理完畢后才安全退出
4)Looper退出后,Handler的發(fā)送的消息會(huì)失敗吼虎,此時(shí)send返回false
5)子線程中如果手動(dòng)創(chuàng)建了Looper犬钢,應(yīng)該在所有事情完成后調(diào)用quit方法來終止消息循環(huán)
7、Looper.loop()的源碼流程?
1)獲取到Looper和消息隊(duì)列
2)for無(wú)限循環(huán)思灰,阻塞于消息隊(duì)列的next方法
3)取出消息后調(diào)用msg.target.dispatchMessage(msg)進(jìn)行消息分發(fā)
8玷犹、
//Looper.java
public static void loop() {
//1. 獲取Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//2. 獲取消息隊(duì)列
final MessageQueue queue = me.mQueue;
......
for (; ; ) {
//3. 獲取消息,如果沒有消息則會(huì)一直阻塞
Message msg = queue.next();
/**=================================
* 4. 如果消息獲得為null洒疚,則退出循環(huán)
* -Looper退出后歹颓,next就會(huì)返回null
*=================================*/
if (msg == null) {
return;
}
......
/**==========================================================
* 5. 處理消息
* -msg.target:是發(fā)送消息的Handler
* -最終在該Looper中執(zhí)行了Handler的dispatchMessage()
* -成功將代碼邏輯切換到指定的Looper(線程)中執(zhí)行
*========================================================*/
msg.target.dispatchMessage(msg);
......
}
}
9、Looper.loop()在什么情況下會(huì)退出油湖?
1)next方法返回的msg == null
2)線程意外終止
10巍扛、Looper.quit/quitSafely的本質(zhì)是什么?
讓消息隊(duì)列的next()返回null乏德,依次來退出Looper.loop()
11电湘、Looper.loop()方法執(zhí)行時(shí),如果內(nèi)部的myLooper()獲取不到Looper會(huì)出現(xiàn)什么結(jié)果?
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
12鹅经、Android如何保證一個(gè)線程最多只能有一個(gè)Looper寂呛?如何保證只有一個(gè)MessageQueue
1)Looper的構(gòu)造方法是private,不能直接構(gòu)造瘾晃。需要通過Looper.prepare()進(jìn)行創(chuàng)建:
2)在Looper.prepare()中會(huì)判斷sThreadLocal.get()是否為null贷痪,若不是,會(huì)拋出異常蹦误,從而保證了一個(gè)線程最多只能有一個(gè)Looper劫拢,也保證了只有一個(gè)MessageQueue肉津。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
13、Handler消息機(jī)制中舱沧,一個(gè)looper是如何區(qū)分多個(gè)Handler的妹沙?
1)msg的target持有一個(gè)發(fā)送此消息的Handler引用。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
2)Looper.loop()會(huì)阻塞于MessageQueue.next()
3)取出msg后熟吏,msg.target成員變量就是該msg對(duì)應(yīng)的Handler
4)調(diào)用msg.target的disptachMessage()進(jìn)行消息分發(fā)距糖。
14、主線程是如何準(zhǔn)備消息循環(huán)的牵寺?
//ActivityThread.java
public static void main(String[] args) {
//1. 創(chuàng)建主線程的Looper和MessageQueue
Looper.prepareMainLooper();
......
//2. 開啟消息循環(huán)
Looper.loop();
}
/**=============================================
* ActivityThread中需要Handler與消息隊(duì)列進(jìn)行交互
* -內(nèi)部定義一系列消息類型:主要有四大組件等
* //ActivityThread.java
*=============================================*/
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
......
}
1悍引、ActivityThread通過ApplicationThread和AMS進(jìn)行IPC通信
2、AMS完成請(qǐng)求的工作后會(huì)回調(diào)ApplicationThread中的Binder方法
3帽氓、ApplicationThread會(huì)向Handler H發(fā)送消息
4趣斤、H接收到消息后會(huì)將ApplicationThread的邏輯切換到ActivityThread中去執(zhí)行
15黎休、主線程Looper一直循環(huán)查消息為何沒卡主線程浓领?
1)線程的阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行势腮。直到線程進(jìn)入就緒狀態(tài)联贩,才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。
(2)主線程Looper從消息隊(duì)列讀取消息嫉鲸,當(dāng)讀完所有消息時(shí)撑蒜,主線程阻塞。子線程往消息隊(duì)列發(fā)送消息玄渗,并且往管道文件寫數(shù)據(jù)座菠,主線程即被喚醒,從管道文件讀取數(shù)據(jù)藤树,主線程被喚醒只是為了讀取消息浴滴,當(dāng)消息讀取完畢,再次睡眠岁钓。因此loop的循環(huán)并不會(huì)對(duì)CPU性能有過多的消耗升略。
四、ThreadLocal
1屡限、ThreadLocal是什么品嚣?有什么用?
ThreadLocal是線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類钧大,可以在指定線程中存儲(chǔ)數(shù)據(jù)翰撑,之后只有在指定線程中才可以讀取到存儲(chǔ)的數(shù)據(jù)
2、ThreadLocal的兩個(gè)應(yīng)用場(chǎng)景啊央?
1)某些數(shù)據(jù)是以線程為作用域眶诈,并且不同線程具有不同的數(shù)據(jù)副本的時(shí)候涨醋。ThreadLocal可以輕松實(shí)現(xiàn)Looper在線程中的存取。
2)在復(fù)雜邏輯下的對(duì)象傳遞逝撬,通過ThreadLocal可以讓對(duì)象成為線程內(nèi)的全局對(duì)象浴骂,線程內(nèi)部通過get就可以獲取。
3宪潮、ThreadLocal的使用
mBooleanThreadLocal.set(true);
Log.d("ThreadLocal", "[Thread#main]" + mBooleanThreadLocal.get());
new Thread("Thread#1"){
@Override
public void run(){
mBooleanThreadLocal.set(false);
Log.d("ThreadLocal", "[Thread#1]" + mBooleanThreadLocal.get());
}
}.start();
new Thread("Thread#2"){
@Override
public void run(){
Log.d("ThreadLocal", "[Thread#2]" + mBooleanThreadLocal.get());
}
}.start();
1溯警、最終main中輸出true; Thread#1中輸出false; Thread#2中輸出null
2、ThreadLocal內(nèi)部會(huì)從各自線程中取出數(shù)組坎炼,再根據(jù)當(dāng)前ThreadLocal的索引去查找出對(duì)應(yīng)的value值愧膀。
4拦键、ThreadLocal的set()源碼分析
//ThreadLocal.java
public void set(T value) {
//1. 獲取當(dāng)前線程
Thread t = Thread.currentThread();
//2. 獲取當(dāng)前線程對(duì)應(yīng)的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//3. map存在就進(jìn)行存儲(chǔ)
map.set(this, value);
else
//4. 不存在就創(chuàng)建map并且存儲(chǔ)
createMap(t, value);
}
//ThreadLocal.java內(nèi)部類: ThreadLocalMap
private void set(ThreadLocal<?> key, Object value) {
//1. table為Entry數(shù)組
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//2. 根據(jù)當(dāng)前ThreadLocal獲取到Hash key谣光,并以此從table中查詢出Entry
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//3. 如果Entry的ThreadLocal與當(dāng)前的ThreadLocal相同,則用新值覆蓋e的value
if (k == key) {
e.value = value;
return;
}
//4. Entry沒有ThreadLocal則把當(dāng)前ThreadLocal置入芬为,并存儲(chǔ)value
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//5. 沒有查詢到Entry萄金,則新建Entry并且存儲(chǔ)value
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//ThreadLocal內(nèi)部類ThreadLocalMap的靜態(tài)內(nèi)部類
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
5、ThreadLocal的get()源碼分析
public T get() {
//1. 獲取當(dāng)前線程對(duì)應(yīng)的ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//2. 取出map中的對(duì)應(yīng)該ThreadLocal的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
//3. 獲取到entry后返回其中的value
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//4. 沒有ThreadLocalMap或者沒有獲取到ThreadLocal對(duì)應(yīng)的Entry媚朦,返回規(guī)定數(shù)值
return setInitialValue();
}
private T setInitialValue() {
//1. value = null
T value = initialValue();//返回null
Thread t = Thread.currentThread();
//2. 若不存在則新ThreadLocalMap, 在里面以threadlocal為key,value為值,存入entry
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
1氧敢、當(dāng)前線程對(duì)應(yīng)了一個(gè)ThreadLocalMap
2、當(dāng)前線程的ThreadLocal對(duì)應(yīng)一個(gè)Map中的Entry(存在table中)
3询张、Entry中key會(huì)獲取其對(duì)應(yīng)的ThreadLocal, value就是存儲(chǔ)的數(shù)值
6孙乖、ThreadLocal的原理
1、thread.threadLocals就是當(dāng)前線程thread中的ThreadLocalMap
2份氧、ThreadLocalMap中有一個(gè)table數(shù)組唯袄,元素是Entry。根據(jù)ThreadLocal(需要轉(zhuǎn)換獲取到Hash Key)能get到對(duì)應(yīng)的Enrty蜗帜。
3恋拷、Entry中key為ThreadLocal, value就是存儲(chǔ)的數(shù)值。
五厅缺、內(nèi)存泄漏
1蔬顾、Handler的內(nèi)存泄漏如何避免?
1湘捎、采用靜態(tài)內(nèi)部類:static handler = xxx
2诀豁、Activity結(jié)束時(shí),調(diào)用handler.removeCallback()窥妇、然后handler設(shè)置為null
3舷胜、如果使用到Context等引用,要使用弱引用
單獨(dú)寫一篇分析秩伞。
參考:
1逞带、Handler消息機(jī)制(50題)
主要就是參考了他的博客欺矫,可以算是轉(zhuǎn)載了吧,他的源碼分析講的挺清楚的展氓。