hello~辨图,大家好班套,我叫石頭。
這幾天在重新梳理Android開發(fā)相關(guān)的知識(shí)點(diǎn)故河,今天在梳理hanlder的時(shí)候吱韭,突然感受是不是可以換一個(gè)角度來梳理handler的原理,這樣可能更有助于理解handler鱼的,接下來設(shè)計(jì)到的知識(shí)點(diǎn)有內(nèi)存理盆, 線程,java內(nèi)存模型等相關(guān)概念凑阶。
假如我們一個(gè)引用的內(nèi)存有這些線程猿规,handler,looper是不是有點(diǎn)暈乎乎的
下面我們就從另外一個(gè)角度切入,看看代碼究竟是怎么流轉(zhuǎn)的师郑。
下面我會(huì)用大量的圖片展示handler在時(shí)間上內(nèi)存的變化模型(內(nèi)存只展示了相關(guān)的變量跟概念)
一個(gè)初始化的項(xiàng)目
當(dāng)我們新建一個(gè)項(xiàng)目环葵,什么代碼都沒寫的時(shí)候,hanlder相關(guān)的內(nèi)存如下
這個(gè)時(shí)候我們有一個(gè)主線程(或者叫UI線程)宝冕,這個(gè)時(shí)候系統(tǒng)會(huì)默認(rèn)幫我們綁定了一個(gè)looper對(duì)象
我們Activity的LifeCycler回調(diào)就是這個(gè)默認(rèn)looper來分發(fā)的张遭。
Q:現(xiàn)在我們模擬一個(gè)場(chǎng)景就是:
界面元素: 1個(gè)Button, 1個(gè)TextView
任務(wù):點(diǎn)擊Button新開一個(gè)子線程地梨,在里面執(zhí)行我們需要的一些耗時(shí)操作菊卷,之后通過主線程更新TextView
我們用偽代碼模擬下
第一步:創(chuàng)建Handler對(duì)象
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
// 處理UI更新
textView.setText("xxx");
break;
}
}
}
這里我們思考一下
handleMessage
里面的代碼是在哪個(gè)線程里面執(zhí)行,如果我們把handler跟另外一個(gè)looper綁定宝剖,又會(huì)在哪個(gè)線程執(zhí)行handleMessage
里面的代碼的烁,后面我會(huì)給出答案.
第二步:添加Button點(diǎn)擊事件
點(diǎn)擊Button新開一個(gè)子線程,在里面執(zhí)行我們需要的一些耗時(shí)操作
Button button = findViewById(R.id.xxx);
button.setOnClickListener(v -> {
new Thread(() -> {
// 做一些耗時(shí)操作
doSomeWork();
});
});
第二步:在子線程中通過hander發(fā)送消息
Button button = findViewById(R.id.xxx);
button.setOnClickListener(v -> {
new Thread(() -> {
// 做一些耗時(shí)操作
doSomeWork();
Message msg = Message.obtain();
msg.what = 1; //消息的標(biāo)識(shí)
msg.obj = "子線程想發(fā)送給主線程的數(shù)據(jù)"; // 消息的存放
handler.sendMessage(msg);
});
});
這里主要想說明2點(diǎn):
1.handler不是線程私有對(duì)象诈闺,所有能在子線程中通過其引用調(diào)用發(fā)送消息的方法.
2.looper是線程私有的渴庆,我們的消息最終會(huì)到達(dá)消息隊(duì)列(消息隊(duì)列在looper對(duì)象中)
在子線程通過handler引用調(diào)用sendMessage方法
第三步:在handler的handleMessage中處理消息
到這里我們的邏輯就完成了
但是我們要思考的是:
其實(shí)我們所謂的在子線程中發(fā)送消息消息,在主線程中處理消息,其實(shí)說白了都是在利用handler對(duì)象來處理的
因?yàn)閔andler沒有對(duì)象到線程中去襟雷,所以任何線程只要能拿到他的引用都能對(duì)其進(jìn)行操作
- 子線程中發(fā)送消息消息:我們知道一個(gè)Thread類的
run
方法是運(yùn)行在子線程中的刃滓,所以在其中執(zhí)行handler.sendMessage(msg);
方法也就是在子線程中。 - 主線程中處理消息: 我們知道
Message
是被looper從MessageQueue
取出耸弄,并且通過其中handler引用
調(diào)用到了handleMessage
中咧虎,looper是被主線程私有的,并且是在主線程中的run
方法中輪詢處理的计呈,所以這段代碼就是在主線程中執(zhí)行的.
總結(jié)一下就是
發(fā)送消息是在子線程的run
方法進(jìn)行的砰诵,處理消息是在主線程的run
方法進(jìn)行的,中間的邏輯紐帶是handler的引用捌显。
所謂的一段代碼在哪個(gè)線程中運(yùn)行茁彭,就是說的這段代碼的調(diào)用鏈?zhǔn)窃谀膫€(gè)線程的run中被調(diào)用的
進(jìn)階思考
Q1: HandlerThread使用中的handler中的sendMessage是在哪個(gè)線程中?
Q2: HandlerThread使用中的handler中的handleMessage是在哪個(gè)線程中扶歪?
Q3: 怎么在子線程1中發(fā)送消息理肺,在子線程2中處理消息?
Q4: 一個(gè)線程能有幾個(gè)looper善镰,幾個(gè)handler(這個(gè)問法不太嚴(yán)謹(jǐn)妹萨,想想為啥)?