當前位置:首頁 » 安卓系統 » android非同步handler

android非同步handler

發布時間: 2024-03-28 06:02:44

1. 深入分析Android-Handler消息機制

Handler是Android消息機制的上層介面。通過它可以輕松地將一個任務切換到Handler所在的線程中去執行。通常情況下,Handler的使用場景就是 更新UI 。

在子線程中,進行耗時操作,執行完操作後,發送消息,通知主線程更新UI。

Handler消息機制主要包括: MessageQueue 、 Handler 、 Looper 這三大部分,以及 Message 。

從上面的類圖可以看出:

MessageQueue、Handler和Looper三者之間的關系: 每個線程中只能存在一個Looper,Looper是保存在ThreadLocal中的。 主線程(UI線程)已經創建了一個Looper,所以在主線程中不需要再創建Looper,但是在其他線程中需要創建Looper。 每個線程中可以有多個Handler,即一個Looper可以處理來自多個Handler的消息。 Looper中維護一個MessageQueue,來維護消息隊列,消息隊列中的Message可以來自不同的Handler。

在子線程執行完耗時操作,當Handler發送消息時,將會調用 MessageQueue.enqueueMessage ,向消息隊列中添加消息。 當通過 Looper.loop 開啟循環後,會不斷地從消息池中讀取消息,即調用 MessageQueue.next , 然後調用目標Handler(即發送該消息的Handler)的 dispatchMessage 方法傳遞消息, 然後返回到Handler所在線程,目標Handler收到消息,調用 handleMessage 方法,接收消息,處理消息。

從上面可以看出,在子線程中創建Handler之前,要調用 Looper.prepare() 方法,Handler創建後,還要調用 Looper.loop() 方法。而前面我們在主線程創建Handler卻不要這兩個步驟,因為系統幫我們做了。

初始化Looper

從上可以看出,不能重復創建Looper,每個線程只能創建一個。創建Looper,並保存在 ThreadLocal 。其中ThreadLocal是線程本地存儲區(Thread Local Storage,簡稱TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。

開啟Looper

創建Handler

發送消息

post方法:

send方法:

2. Android面試 Handler機制

Handler就是解決線程與線程間的通信。
當我們在子線程處理耗時操作,耗時操作完成後我們需要更新UI的時候,這就是需要使用Handler來處理了,因為子線程不能更 新UI,Handler能讓我們容易的把任務切換回來它所在的線程。
消息處理機制本質:一個線程開啟循環模式持續監聽並依次處理其他線程給它發的消息。

一個線程可以有多個Handler,通過new Handler的方式創建。

一個線程只能有一個Looper,通過Looper.perpare方法會創建一個Looper保存在ThreadLocal中,每個線程都有一個LocalThreadMap,會將Looper保存在對應線程中的LocalThreadMap,key為ThreadLocal,value為Looper。

內部類持有外部類的對象,handler持有activity的對象,當頁面activity關閉時,handler還在發送消息,handler持有activity的對象,導致handler不能及時被回收,所以造成內存泄漏。

因為當handler發送消息時,會有耗時操作,並且會利用線程中的looper和messageQueue進行消息發送,looper和messageQueue的生命周期是很長的,和application一樣,所以handler不容易被銷毀,所以造成內存泄漏。

解決方案有:

可以在子線程中創建Handler,我們需要調用Looper.perpare和Looper.loop方法。或者通過獲取主線程的looper來創建Handler。

應該調用Looper的quit方法,因為可以將looper中的messageQueue里的message都移除掉,並且將內存釋放。

通過synchronized鎖機制保證線程安全。

Message.obtain來創建Message。這樣會復用之前的Message的內存,不會頻繁的創建對象,導致內存抖動。

點擊按鈕的時候會發送消息到Handler,但是為了保證優先執行,會加一個標記非同步,同時會發送一個target為null的消息,這樣在使用消息隊列的next獲取消息的時候,如果發現消息的target為null,那麼會遍歷消息隊列將有非同步標記的消息獲取出來優先執行,執行完之後會將target為null的消息移除。(同步屏障)

更多內容戳這里(整理好的各種文集)

3. Android Handler那些事兒,消息屏障IdelHandlerANR

Handler 是Android SDK中用來處理非同步消息的核心類,子線程可以通過handler來通知主線程進行ui更新。

備註:本文源碼截圖 基於Android sdk 28

Handler機制 消息發送主要流程如圖

應用程序啟動後,zygote fork一個應用進程後,和普通java程序一樣,程序會首先執行ActivityThread中的main函數。在main函數中,程序首先會創建Looper對象並綁定到主線程中,然後開啟loop循環。(ps:主線程loop循環不能退出)

在prepareMainLooper方法中,最終會創建Looper,MessageQueue對象 以及創建native層MessageQueue對象。

使用Handler.sendMessageXXX或這 postDedayXXX發送消息後,最終會調用到SendMessageAtTime方法中。

然後調用MessageQueue.enqueueMessage將消息存到消息隊列中。

存入消息後,然後通過調用native方法 喚醒主線程進行消息處理。

當應用程序啟動,做完一些必要工作之後,便會開啟Loop循環,除非系統異常,否則該循環不會停止。loop循環中,主要做兩件事,第一,從消息隊列中取消息。第二,進行消息分發處理。

MessageQueue.next() 方法 通過調用 native方法 nativePollOnce(ptr, nextPollTimeoutMillis)實現無消息處理時,進入阻塞的功能。
當nextPollTimeoutMillis 值為0時,該方法會立刻返回;
當nextPollTimeoutMillis 值為-1時,該方法會無限阻塞,直到被喚醒;
當nextPollTimeoutMillis 值大於0時,該方法會將該值設置為超時時間,阻塞到達一定時間後,返回;

在loop循環中 ,通過調用 msg.target.dispatchMessage(msg) 進行消息的分發處理

使用當前線程的MessageQueue.addIdleHandler方法可以在消息隊列中添加一個IdelHandler。

當MessageQueue 阻塞時,即當前線程空閑時,會回調IdleHandler中的方法;

當IdelHandler介面返回false時,表示該IdelHandler只執行一次,

a,延遲執行

例如,當啟動Activity時,需要延時執行一些操作,以免啟動過慢,我們常常使用以下方式延遲執行任務,但是在延遲時間上卻不好控制。

其實,這時候使用IdelHandler 會更優雅

b,批量任務,任務密集,且只關注最終結果

例如,在開發一個IM類型的界面時,通常情況下,每次收到一個IM消息時,都會刷新一次界面,但是當短時間內, 收到多條消息時,就會刷新多次界面,容易造成卡頓,影響性能,此時就可以使用一個工作線程監聽IM消息,在通過添加IdelHandler的方式通知界面刷新,避免短時間內多次刷新界面情況的發生。

在Android的消息機制中,其實有三種消息: 普通消息、非同步消息及消息屏障。

消息屏障 也是一種消息,但是它的target為 null。可以通過MessageQueue中的postSyncBarrier方法發送一個消息屏障(該方法為私有,需要反射調用)。

在消息循環中,如果第一條消息就是屏障消息,就往後遍歷,看看有沒有非同步消息:
如果沒有,則無限休眠,等待被喚醒
如果有,就看離這個消息被觸發時間還有多久,設置一個超時時間,繼續休眠

非同步消息 和普通消息一樣,只不過它被設置setAsynchronous 為true。有了這個標志位,消息機制會對它有些特別的處理,我們稍後說。

所以 消息屏障和非同步消息的作用 很明顯,在設置消息屏障後,非同步消息具有優先處理的權利。

這時候我們回顧將消息添加到消息隊列中時,可以發現,其實並不是每一次添加消息時,都會喚醒線程。
當該消息插入到隊列頭時,會喚醒該線程;
當該消息沒有插入到隊列頭,但隊列頭是屏障,且該消息是隊列中 靠前的一個非同步消息,則會喚醒線程,執行該消息;

調用MessageQueue.removeSyncBarrier 方法可以移除指定的消息屏障

ANR 即 Application Not Response, 是系統進程對應用行為的一種監控,如果應用程序沒有在規定時間內完成任務的話,就會引起ANR。

ANR類型

Service Timeout : 前台服務20s, 後台服務200s

BroadcastQueue Timeout : 前台廣播 10s,後台廣播60s

ContentPrivider Timeout : 10s

InputDispatching Timeout : 5s

比如,在啟動一個服務時, AMS端通過應用進程的Binder對象創建Service, 在scheleCreateService()方法中 會調用到當前service的onCreate()生命周期函數;

bumpServiceExecutingLocked()方法內部實際上會調用到scheleServiceTimeoutLocked()方法,發送一個ActivityManagerService.SERVICE_TIMEOUT_MSG類型消息到AMS工作線程中。

消息的延時時間,如果是前台服務,延時20s, 如果是後台服務,延時200s;

如果Service的創建 工作在 上訴消息的延時時間內完成,則會移除該消息,

否則,在Handler正常收到這個消息後,就會進行服務超時處理,即彈出ANR對話框。

復雜情況下,可能會頻繁調用sendMessage 往消息隊列中,添加消息,導致消息積壓,造成卡頓,

1,重復消息過濾

頻繁發送同類型消息時,有可能隊列中之前的消息還沒有處理,又發了一條相同類型的消息,更新之前的數據,這時候,可以採用移除前一個消息的方法,優化消息隊列。

2,互斥消息取消

在發送消息時,優先將消息隊列中還未處理的信息已經過時的消息 移除,優化隊列

3,隊列優化-復用消息

創建消息時,優先採用之前回收的消息,避免重復創建對象,引起GC

完~
(如果錯誤或不足,望指出, 大家共同進步)

4. [Android源碼分析] - 非同步通信Handler機制

一、問題:在Android啟動後會在新進程里創建一個主線程,也叫UI線程( 非線程安全 )這個線程主要負責監聽屏幕點擊事件與界面繪制。當Application需要進行耗時操作如網路請求等,如直接在主線程進行容易發生ANR錯誤。所以會創建子線程來執行耗時任務,當子線程執行完畢需要通知UI線程並修改界面時,不可以直接在子線程修改UI,怎麼辦?

解決方法:Message Queue機制可以實現子線程與UI線程的通信。

該機制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable對象 發給Looper,由它把消息放入所屬線程的消息隊列中,然後Looper又會自動把消息隊列里的消息/Runnable對象 廣播 到所屬線程里的Handler,由Handler處理接收到的消息或Runnable對象。

1、Handler

每次創建Handler對象時,它會自動綁定到創建它的線程上。如果是主線程則默認包含一個Message Queue,否則需要自己創建一個消息隊列來存儲。

Handler是多個線程通信的信使。比如在線程A中創建AHandler,給它綁定一個ALooper,同時創建屬於A的消息隊列AMessageQueue。然後在線程B中使用AHandler發送消息給ALooper,ALooper會把消息存入到AMessageQueue,然後再把AMessageQueue廣播給A線程里的AHandler,它接收到消息會進行處理。從而實現通信。

2、Message Queue

在主線程里默認包含了一個消息隊列不需要手動創建。在子線程里,使用Looper.prepare()方法後,會先檢查子線程是否已有一個looper對象,如果有則無法創建,因為每個線程只能擁有一個消息隊列。沒有的話就為子線程創建一個消息隊列。

Handler類包含Looper指針和MessageQueue指針,而Looper里包含實際MessageQueue與當前線程指針。

下面分別就UI線程和worker線程講解handler創建過程:

首先,創建handler時,會自動檢查當前線程是否包含looper對象,如果包含,則將handler內的消息隊列指向looper內部的消息隊列,否則,拋出異常請求執行looper.prepare()方法。

 - 在 UI線程 中,系統自動創建了Looper 對象,所以,直接new一個handler即可使用該機制;

- 在 worker線程 中,如果直接創建handler會拋出運行時異常-即通過查『線程-value』映射表發現當前線程無looper對象。所以需要先調用Looper.prepare()方法。在prepare方法里,利用ThreadLocal<Looper>對象為當前線程創建一個Looper(利用了一個Values類,即一個Map映射表,專為thread存儲value,此處為當前thread存儲一個looper對象)。然後繼續創建handler, 讓handler內部的消息隊列指向該looper的消息隊列(這個很重要,讓handler指向looper里的消息隊列,即二者共享同一個消息隊列,然後handler向這個消息隊列發送消息,looper從這個消息隊列獲取消息) 。然後looper循環消息隊列即可。當獲取到message消息,會找出message對象里的target,即原始發送handler,從而回調handler的handleMessage() 方法進行處理。

 - handler與looper共享消息隊列 ,所以handler發送消息只要入列,looper直接取消息即可。

 - 線程與looper映射表 :一個線程最多可以映射一個looper對象。通過查表可知當前線程是否包含looper,如果已經包含則不再創建新looper。

5、基於這樣的機制是怎樣實現線程隔離的,即在線程中通信呢。 

核心在於 每一個線程擁有自己的handler、message queue、looper體系 。而 每個線程的Handler是公開 的。B線程可以調用A線程的handler發送消息到A的共享消息隊列去,然後A的looper會自動從共享消息隊列取出消息進行處理。反之一樣。

二、上面是基於子線程中利用主線程提供的Handler發送消息出去,然後主線程的Looper從消息隊列中獲取並處理。那麼還有另外兩種情況:

1、主線程發送消息到子線程中;

採用的方法和前面類似。要在子線程中實例化AHandler並設定處理消息的方法,同時由於子線程沒有消息隊列和Looper的輪詢,所以要加上Looper.prepare(),Looper.loop()分別創建消息隊列和開啟輪詢。然後在主線程中使用該AHandler去發送消息即可。

2、子線程A與子線程B之間的通信。

1、 Handler為什麼能夠實現不同線程的通信?核心點在哪?

不同線程之間,每個線程擁有自己的Handler、消息隊列和Looper。Handler是公共的,線程可以通過使用目標線程的Handler對象來發送消息,這個消息會自動發送到所屬線程的消息隊列中去,線程自帶的Looper對象會不斷循環從裡面取出消息並把消息發送給Handler,回調自身Handler的handlerMessage方法,從而實現了消息的線程間傳遞。

2、 Handler的核心是一種事件激活式(類似傳遞一個中斷)的還是主要是用於傳遞大量數據的?重點在Message的內容,偏向於數據傳輸還是事件傳輸。

目前的理解,它所依賴的是消息隊列,發送的自然是消息,即類似事件中斷。

0、 Android消息處理機制(Handler、Looper、MessageQueue與Message)

1、 Handler、Looper源碼閱讀

2、 Android非同步消息處理機制完全解析,帶你從源碼的角度徹底理解

謝謝!

wingjay

![](https://avatars0.githubusercontent.com/u/9619875?v=3&s=460)

5. 能講講Android的Handler機制嗎

Android的Handler機制是通俗講為了互相發消息,一般是子線程給主線程發消息完成相應操作。

安卓中最常見的操作是子線程操作完事後得到數據想更新UI,安卓有規定不允許在子線程中刷新UI,所以Handler出現了。

使用和理解大致步驟。

  1. 創建全局Handler對象handler,然後在主線程中初始化它(一般在oncreate中),把它的handmessage裡面的方法重寫,這個方法是收到子線程發給它的消息後執行的邏輯。

  2. 在子線程中獲取數據,調用handler.sendmessage,把要發的消息放在message中。message會添加到Messagequue(消息隊列中,handler創建就帶的)。

3.對象handler被創建和初始化的時候,系統自動會啟動Handler.looper,也就是一個消息輪詢器,它不斷的去查看有沒有消息進入到Messagequue(消息隊列中),有就取出交給handler的handmessage去處理。//這段邏輯是系統自動執行,理解就行。*純手打,不騙人~~~

6. Android的handler機制的原理

Android的handler機制的原理分為非同步通信准備,消息發送,消息循環,消息處理。

1、非同步通信准備

在主線程中創建處理器對象(Looper)、消息隊列對象(Message Queue)和Handler對象。

2、消息入隊

工作線程通過Handler發送消息(Message) 到消息隊列(Message Queue)中。

3、消息循環

消息出隊: Looper循環取出消息隊列(Message Queue) 中的的消息(Message)。

消息分發: Looper將取出的消息 (Message) 發送給創建該消息的處理者(Handler)。

4、消息處理

處理者(Handler) 接收處理器(Looper) 發送過來的消息(Message),根據消息(Message) 進行U操作。

handler的作用

handler是android線程之間的消息機制,主要的作用是將一個任務切換到指定的線程中去執行,(准確的說是切換到構成handler的looper所在的線程中去出處理)android系統中的一個例子就是主線程中的所有操作都是通過主線程中的handler去處理的。

Handler的運行需要底層的 messagequeue和 looper做支撐。



7. Android-Handler同步屏障

消息機制的同步屏障,其實就是阻礙同步消息,只讓非同步消息通過。而開啟同步屏障的方法就是調用下面的方法:

源碼如下:

在這里可以看到,Message對象初始化的時候並沒有給target賦值,因此target==null的來源就找得到了。這樣就可以插入一條target==null的消息,這個消息就是一個同步屏障。
那麼開啟消息屏障後,所謂的非同步消息又是如何處理的呢?
消息的最終處理其實都是在消息輪詢器Looper#loop()中,而loop()循環中會調用MessageQueue#next()從消息隊列中進行取消息。

從上面的MessageQueue.next方法可以看出,當消息隊列開啟同步屏障的時候(即標識為msg.target==null),消息機制在處理消息的時候,會優先處理非同步消息。這樣,同步屏障就起到了一種過濾和優先順序的作用。

如果上圖所示,在消息隊列中有同步消息和非同步消息(黃色部分)以及一道牆(同步屏障--紅色部分)。有了同步屏障的存在,msg_2和msg_M這兩個非同步消息可以被優先處理,而後面的msg_3等同步消息則不會被處理。那麼這些同步消息什麼時候可以被處理呢?就需要先移除這個同步屏障,即調用MessageQueue#removeSyncBarrier()

同步屏障一般在日常開發中比較少用,而在系統源碼中就有使用。Android系統中的UI更新相關的消息即為非同步消息,需要優先處理。
16ms左右刷新UI,而是60hz的屏幕,即1s刷新60次。
在Android中什麼是非同步消息?即給:

比如,在View更新時,draw、requestLayout、invalidate等很多地方都調用了。ViewRootImpl#scheleTraversals()。

在這里,mChoreographer.postCallback最終會執行到了Choreographer#postCallbackDelayedInternal()

可以看到,這里就開啟了同步屏障,並且發送了非同步消息。由於UI相關的消息是優先順序最高的,這樣系統就會優先處理這些非同步消息。
最後,當然要移除同步屏障的時候,調用ViewRootImpl#unscheleTraversals

在ViewRootImpl中的doTraversal()方法中也會移除同步屏障,這里移除是因為requestLayout或者invalidate的時候,刷新之後,在doTraversal()中就會移除同步屏障,因為此時消息已經發送並且處理了。

熱點內容
微信限額上傳 發布:2024-04-27 17:55:18 瀏覽:777
支持編譯成機器碼的語言 發布:2024-04-27 17:31:59 瀏覽:745
串口伺服器ip限制 發布:2024-04-27 17:30:20 瀏覽:501
sql編程入門經典 發布:2024-04-27 17:28:52 瀏覽:282
紅木卧室有哪些配置 發布:2024-04-27 17:09:47 瀏覽:855
中心機編程 發布:2024-04-27 17:00:11 瀏覽:116
lms濾波演算法 發布:2024-04-27 16:55:37 瀏覽:444
蘋果電腦遠程桌面連接伺服器 發布:2024-04-27 16:53:08 瀏覽:234
為什麼安卓手機沒有回響 發布:2024-04-27 16:53:08 瀏覽:375
搭建郵箱中繼伺服器 發布:2024-04-27 16:40:42 瀏覽:198