摘要:本文主要說明內(nèi)部消息機制用模擬進行演示并用圖簡單說明并與生產(chǎn)者與消費者模型進行比對代碼地址需要解決的問題主線程怎樣跟子線程進行通信子線程生產(chǎn)的資源如何傳給主線程子線程如何進行等待完成耗時操作才給主線程傳遞消息為何只能在主線程才能創(chuàng)建子線程想
本文主要說明android內(nèi)部消息機制,用java模擬進行演示,并用圖簡單說明.并與生產(chǎn)者與消費者模型進行比對;
git代碼地址
需要解決的問題: 1,主線程怎樣跟子線程進行通信,子線程生產(chǎn)的資源,如何傳給主線程? 2,子線程如何進行等待,完成耗時操作才給主線程傳遞消息? 3,為何只能在主線程才能創(chuàng)建handler,子線程想創(chuàng)建該怎么辦? 4,主線程如何與handler/message/looper...進行結(jié)合的? 建議帶著問題直接看源碼,放到eclipse或者as或者intellj中實際運行后,理解handler運行原理;1 生產(chǎn)者與消費者模型:
github地址:生產(chǎn)者與消費者
代碼中展示,生產(chǎn)者與消費者是分別是兩個線程,生產(chǎn)或者消耗的資源是num,而Resource3類是個生產(chǎn)線(queue),
通過java的BlockingQueue對生產(chǎn)與消費進行阻塞,最終通過main方法創(chuàng)建線程進行生產(chǎn)與消費;
github代碼
看代碼后理解下圖:
其中子線程的是真正的生產(chǎn)者,把生產(chǎn)后的結(jié)果給了message.obj,然后handler.sendMessage進行入列messagequeue(給了生產(chǎn)線);
@Override public void onResponse(Call call, Response response) throws IOException { final String json = response.body().string(); //Log.e("json", "success: "+json.toString() ); if (null!=json && !"".equals(json)&&(!json.contains("請求錯誤")&&(!json.contains("ConnectionRefused")))) alarmList = GsonUtils.jsonToList(json, Alarm.class); Message message = Message.obtain(); message.what = 0 ; message.obj = alarmList; handler.sendMessage(message); }
之后looper進行dispatch之后,再進行handlerMessage在handler進行消費;可見,android中handler的機制與生產(chǎn)者消費者模型最大區(qū)別是生產(chǎn)者消費者分別是兩個線程,而handler既扮演生產(chǎn)者又是消費者卻只是主線程的對象!3,為何只能在主線程才能創(chuàng)建handler....? 4,主線程如何與handler/message/looper...進行結(jié)合的?(通過threadlocal這個map)
handler是通過消息傳遞(message)把json解析出來的東西,傳遞給主線程的messagequeue,然后handler又自己進行處理,這當(dāng)中handler必須等待子線程生產(chǎn)完成(耗時操作完成);
文章開始提出的第二個問題:2,子線程如何進行等待,完成耗時操作才給主線程傳遞消息? 其中原理不是利用java的blockqueue之類.其中messagequeue是個鏈表,阻塞機制與底層的C++相關(guān)聯(lián): 深入理解messagequeue這文章沒有看懂,希望可以拋磚引玉;
另外,handler處理完message之后如何又循環(huán)把message填入隊尾的機制也不是很理解;
如下圖解釋:handler為何只能在主線程以及如何保生產(chǎn)消費是同一個handler
其中l(wèi)ooper的角色是管理既是管理messagequeue的,又是分發(fā)消息給handler的中間者,
handler類圖如下,可見handler中既有l(wèi)ooper,又有messagequeue作為成員變量
主線的main方法中有l(wèi)ooper.papare(),子線程沒有,給localThread這個map進行設(shè)置值:
threadlocal
源碼中,主線程:
在程序啟動的時候,系統(tǒng)已經(jīng)幫我們自動調(diào)用了Looper.prepare()方法。查看ActivityThread中的main()
public static void main(String[] args) { SamplingProfilerIntegration.start(); CloseGuard.setEnabled(false); Environment.initForCurrentUser(); EventLogger.setReporter(new EventLoggingReporter()); Process.setArgV0(""); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
請注意Looper.prepareMainLooper():
public static final void prepareMainLooper() { prepare(); //這步相當(dāng)于對threadlocal進行set,key=主線程,val=looper(looper又有messagequeue) setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } }
原理基本結(jié)束:其他的運用如下,道理也是相通的,
理解AsyncTask的源碼
Android:主線程如何向子線程發(fā)送消息
至于hander.post()的有不錯的文章:Handler中post方法的調(diào)用流程和使用場景
message入隊的時候可能是按照某種順序直接添加,但是出隊的時候可能是按照子線程生產(chǎn)產(chǎn)品時間最短的先出隊進行處理(或者隊列重排序(個人感覺可能性不大)),這點與生產(chǎn)消費的
模型完全不同,如果按照生產(chǎn)一個,消費一個,生產(chǎn)時間長的排在前面,先來后到的話,生產(chǎn)時間短的排在后面就會一直得不到消費,用戶體驗一定差;
這個時候,一定需要一個電話號碼(message.what),這時候handler就相當(dāng)于交換機,通知消費者該如何處理,因為子線程的生產(chǎn)時間是隨機,誰也不知道m(xù)essagequeue排序是如何的,開發(fā)者不能根據(jù)子線程耗時長短估計處理順序,萬一遇到處理時間相同,消費邏輯不同又該怎么辦,所以一定要用what區(qū)分。
一開始感覺安卓完全可以做個標(biāo)志,類似于msg.what的標(biāo)志,作為一個map的key直接壓入隊列messagequeue,之后主線程直接取這個get這個標(biāo)志就可以了,然后自己寫對應(yīng)的邏輯就可以了,但這會造成在子線程主線程直接與messageQueue進行交互,既要處理子線程間的競爭,這時又要對主線程進行休眠喚醒等操作,對messageQueue的開啟無限循環(huán)再get到相應(yīng)的message,還要處理對子線程的操作,這些復(fù)雜的操作都在與主線程的交互sendMessage的時候完成;
當(dāng)然文章開始提出的那幾個問題沒有完全解決,但是通過這篇文章,能看到安卓中UI線程與子線程交互的巧妙之處;
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72308.html
摘要:原文地址游客前言金三銀四,很多同學(xué)心里大概都準(zhǔn)備著年后找工作或者跳槽。最近有很多同學(xué)都在交流群里求大廠面試題。 最近整理了一波面試題,包括安卓JAVA方面的,目前大廠還是以安卓源碼,算法,以及數(shù)據(jù)結(jié)構(gòu)為主,有一些中小型公司也會問到混合開發(fā)的知識,至于我為什么傾向于混合開發(fā),我的一句話就是走上編程之路,將來你要學(xué)不僅僅是這些,豐富自己方能與世接軌,做好全棧的裝備。 原文地址:游客kutd...
摘要:子線程往消息隊列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢,再次睡眠。因此的循環(huán)并不會對性能有過多的消耗。 說下你所知道的設(shè)計模式與使用場景 a.建造者模式: 將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。 使用場景比如最常見的AlertDialog,拿我們開發(fā)過程中舉例,比如Camera...
閱讀 3146·2021-11-08 13:18
閱讀 2287·2019-08-30 15:55
閱讀 3609·2019-08-30 15:44
閱讀 3072·2019-08-30 13:07
閱讀 2784·2019-08-29 17:20
閱讀 1951·2019-08-29 13:03
閱讀 3413·2019-08-26 10:32
閱讀 3229·2019-08-26 10:15