Android開發(fā)中Looper.prepare()和Looper.loop()
什么時(shí)候需要 Looper
Looper用于封裝了android線程中的消息循環(huán),默認(rèn)情況下一個(gè)線程是不存在消息循環(huán)(message loop)的,需要調(diào)用Looper.prepare()來給線程創(chuàng)建一個(gè)消息循環(huán),調(diào)用Looper.loop()來使消息循環(huán)起作用,使用Looper.prepare()和Looper.loop()創(chuàng)建了消息隊(duì)列就可以讓消息處理在該線程中完成。
使用Looper需要注意什么
寫在Looper.loop()之后的代碼不會(huì)被立即執(zhí)行,當(dāng)調(diào)用后mHandler.getLooper().quit()后,loop才會(huì)中止,其后的代碼才能得以運(yùn)行。Looper對象通過MessageQueue來存放消息和事件。一個(gè)線程只能有一個(gè)Looper,對應(yīng)一個(gè)MessageQueue。
比如下面的代碼,只要調(diào)用了getLooper().quit()后代碼2才會(huì)執(zhí)行。
class LooperThread extends Thread { 4 public void run() { Looper.prepare(); //代碼1.... Looper.loop(); //代碼2.... } }
警惕線程未終止造成的內(nèi)存泄露;譬如在Activity中關(guān)聯(lián)了一個(gè)生命周期超過Activity的Thread,在退出Activity時(shí)切記結(jié)束線程。一個(gè)典型的例子就是HandlerThread的run方法是一個(gè)死循環(huán),它不會(huì)自己結(jié)束,線程的生命周期超過了Activity生命周期,我們必須手動(dòng)在Activity的銷毀方法中中調(diào)運(yùn)thread.getLooper().quit();才不會(huì)泄露。
Looper與Activity
Activity的MainUI線程默認(rèn)是有消息隊(duì)列的。所以在Activity中新建Handler時(shí),不需要先調(diào)用Looper.prepare()
主線程中的Looper.loop()一直無限循環(huán)為什么不會(huì)造成ANR
ActivityThread.java 是主線程入口的類,這里你可以看到寫Java程序中司空見慣的main方法,而main方法正是整個(gè)Java程序的入口。
ActivityThread源碼
public static final void main(String[] args) { ... //創(chuàng)建Looper和MessageQueue Looper.prepareMainLooper(); ... //輪詢器開始輪詢 Looper.loop(); ... }
Looper.loop()方法
while (true) { //取出消息隊(duì)列的消息,可能會(huì)阻塞 Message msg = queue.next(); // might block ... //解析消息,分發(fā)消息 msg.target.dispatchMessage(msg); ... }
ActivityThread的main方法主要就是做消息循環(huán),一旦退出消息循環(huán),那么你的應(yīng)用也就退出了。那為什么這個(gè)死循環(huán)不會(huì)造成ANR異常呢?
因?yàn)锳ndroid 的是由事件驅(qū)動(dòng)的,looper.loop() 不斷地接收事件、處理事件,每一個(gè)點(diǎn)擊觸摸或者說Activity的生命周期都是運(yùn)行在 Looper.loop() 的控制之下,如果它停止了,應(yīng)用也就停止了。只能是某一個(gè)消息或者說對消息的處理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。也就說我們的代碼其實(shí)就是在這個(gè)循環(huán)里面去執(zhí)行的,當(dāng)然不會(huì)阻塞了。
handleMessage方法部分源碼
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord) msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case PAUSE_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0); maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case PAUSE_ACTIVITY_FINISHING: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; ........... } }
可以看見Activity的生命周期都是依靠主線程的Looper.loop,當(dāng)收到不同Message時(shí)則采用相應(yīng)措施。
如果某個(gè)消息處理時(shí)間過長,比如你在onCreate(),onResume()里面處理耗時(shí)操作,那么下一次的消息比如用戶的點(diǎn)擊事件不能處理了,整個(gè)循環(huán)就會(huì)產(chǎn)生卡頓,時(shí)間一長就成了ANR。
相關(guān)文章
Android開源AndroidSideMenu實(shí)現(xiàn)抽屜和側(cè)滑菜單
這篇文章主要為大家詳細(xì)介紹了Android開源AndroidSideMenu實(shí)現(xiàn)抽屜和側(cè)滑菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02淺談onTouch先執(zhí)行,還是onClick執(zhí)行(詳解)
onTouch先執(zhí)行,還是onClick執(zhí)行?下面小編就為大家?guī)硪黄獪\談onTouch先執(zhí)行,還是onClick執(zhí)行(詳解)。希望對大家有所幫助。一起跟隨小編過來看看吧2017-03-03解決android studio android monitor打不開的問題
下面小編就為大家分享一篇解決android studio android monitor打不開的問題,具有很的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01Android中Glide加載圖片并實(shí)現(xiàn)圖片緩存
本篇文章主要介紹了Android中Glide加載圖片并實(shí)現(xiàn)圖片緩存,這里和大家簡單的分享一下Glide的使用方法以及緩存 ,有興趣的可以了解一下。2017-03-03從源代碼分析Android Universal ImageLoader的緩存處理機(jī)制
這篇文章主要介紹了從源代碼分析Android Universal ImageLoader的緩存處理機(jī)制 的相關(guān)資料,需要的朋友可以參考下2016-01-01android TextView 設(shè)置和取消刪除線的兩種方法
這篇文章主要介紹了android TextView 設(shè)置和取消刪除線的兩種方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03Android開發(fā)學(xué)習(xí)路線的七大階段
這篇文章主要介紹了Android開發(fā)學(xué)習(xí)路線的七大階段,本文講解了Java面向?qū)ο缶幊獭ava Web開發(fā)、android UI編程、android網(wǎng)絡(luò)編程與數(shù)據(jù)存儲(chǔ)、android手機(jī)硬件管理等七大階段,需要的朋友可以參考下2015-04-04Android判斷手機(jī)是否是小米MIUI系統(tǒng)的方法
這篇文章主要介紹了Android判斷手機(jī)是否是小米MIUI系統(tǒng)的方法的相關(guān)資料,需要的朋友可以參考下2016-02-02