android消息處理
A. 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做支撐。
B. Android——消息分發機制
什麼是 Handler 機制 ?
Handler 機制是 Android 中用於 線程間通信 的一套通信機制。
為什麼是 Handler ?Handler 機制為什麼被那麼多次的提及 ?
從Android4.0開始,Android 中網路請求強制不允許在主線程中操作,而更新UI的操作則不允許在子線程中執行。當在子線程中執行網路請求,拿到伺服器返回的數據之後,要更新UI。由於系統的要求,勢必會產生一種矛盾:數據在子線程,更新UI要在主線程。此時我們必須要把數據返回到主線程中才行,Handler機制應運而生。
Android 中針對耗時的操作,放在主線程操作,輕者會造成 UI 卡頓,重則會直接無響應,造成 Force Close。同時在 Android 3.0 以後,禁止在主線程進行網路請求。
針對耗時或者網路操作,那就不能在主線程進行直接操作了,需要放在子線程或者是工作線程中進行操作,操作完成以後,再更新主線程即 UI 線程。這里就涉及到一個問題了,在子線程執行完成以後,怎麼能更新到主線程即 UI 線程呢,針對以上問題,就需要用到 Android 的消息機制了,即: Handler, Message, MessageQueue, Looper 全家桶
Handler機制中最重要的四個對象
Handler的構造方法:
Looper :
Handler的使用:
MessageQueue:
Looper.loop()
Handler.dispatchMessage()
handler導致activity內存泄露的原因:
handler發送的消息在當前handler的消息隊列中,如果此時activity finish掉了,那麼消息隊列的消息依舊會由handler進行處理,若此時handler聲明為內部類(非靜態內部類),我們知道內部類天然持有外部類的實例引用,這樣在GC垃圾回收機制進行回收時發現這個Activity居然還有其他引用存在,因而就不會去回收這個Activity,進而導致activity泄露。
假如在子線程執行了耗時操作,這時用戶操作進入了其他的 acitvity, 那麼 MainActivity 就會被內存回收的,但是這個時候發現 Handler 還在引用著 MainActivity,內存無法及時回收,造成內存泄漏。
Handler 防止內存泄漏常見方法:
為什麼通過 Handler 可以把子線程的結果通知或者攜帶給 UI 線程 ?
這里的 Handler 指的是主線程的 Handler ,同時與 Handler 配套的 Looper , MessageQueue 是在 UI 線程初始化的,所以在子線程中調用 Handler 發送消息可以更新 UI 線程。
Looper 在 UI 線程源碼, 在 ActivityThread 類:
C. 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
完~
(如果錯誤或不足,望指出, 大家共同進步)
D. Android消息機制和原理
Android消息機制及其原理
Handle的原理
andriod提供了Handler和Looper來滿足線程間的通信。Handler先進先出原則。Looper類用來管理特定線程內對象之間的消息交換(MessageExchange)。
MessageQueue
MessageQueue是持有Message(在Looper中派發)的一個鏈表,Message並不是直接添加到MessageQueue中的,而是通過與Looper相關聯的Handler來進行的。
用來存放線程放入的消息,讀取會自動刪除消息,單鏈表維護,在插入和刪除上有優勢。在其next()中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。
Looper
一個線程可以產生一個Looper對象,由它來管理此線程里的MessageQueue
Looper創建的時候會創建一個MessageQueue,調用loop()方法的時候消息循環開始,loop()也是一個死循環,會不斷調用messageQueue的next(),當有消息就處理,否則阻塞在messageQueue的next()中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然後loop()方法也跟著退出。
MessageQueue和Looper是一對一關系,Handler和Looper是多對一
Handler
在主線程構造一個Handler,與Looper溝通,以便push新消息到MessageQueue里;
接收Looper從MessageQueue取出Handler所送來的消息。然後在其他線程調用sendMessage(),此時主線程的MessageQueue中會插入一條message,然後被Looper使用.
Thread
UIthread 通常就是main thread,而Android啟動程序時會替它建立一個MessageQueue,系統的主線程在ActivityThread的main()為入口開啟主線程,其中定義了一系列消息類型,包含四大組件的啟動停止。
消息隊列分發演算法源碼
每個message之間拉手,知道自己前面和後面的message
message通過時間戳來排序,小的在前
配合handle取出message,message時間到,就去除隊列首個message,取出之後置為null,第二個message就排在第一,類推
//消息的存放
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.when = when;
Message p = mMessages; //註解1
if (p == null || when == 0 || when < p.when){
msg.next = p;
mMessages = msg; //註解2
} else {
Message prev;
for (;;) { //註解3
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
}
return true;
}
E. Framework事件機制——手撕Android事件處理的三種方法
Android的事件處理的三種方法:
setOnClickListener,setOnLongClickListener、setOnTouchListener
注意:如果onTouchEvent方法return true,則單擊事件和長摁事件不再執行;若onLongClick方法返回true,則單擊事件不再處理。
需要定義繼承組件的類,重寫回調方法Touch方法執行時,先被Activity捕獲,DispatchTouchEvent方法處理。return false,交給上層的onTouchEvent方法處理;return super.dispatchTouchEvent(ev),則傳遞給最外層的View。
View用Dispatch方法處理,return false,由上層的onTouchEvent方法處理。如果返回super.dispatchTouchEvent(ev),則本層的onInterceptTouchEvent攔截,如果攔截true,則攔截,false不攔截,傳遞給子View的DispatchTouchEvent處理。
常用的回調方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(軌跡球事件)監聽和回調同時存在時,先調用監聽。
流程模型圖:
Event source 事件源
Event 事件
Event Listener 事件監聽器
下面我們來看一下點擊事件和觸摸事件的監聽三要素具體是那部分:
由於點擊事件比較簡單,系統已經幫我們處理了,並沒有找到具體事件是哪個。
View.OnClickListener 單擊事件監聽器必須實現的接⼝
View.OnCreateContextMenuListener 創建上下⽂菜單事件
View.OnFocusChangeListener 焦點改變事件
View.OnKeyListener 按鍵事件監聽器
View.OnLongClickListener 長按事件監聽器
View.OnTouchListener 觸摸屏事件監聽器
⾸先,事件監聽機制中由事件源,事件,事件監聽器三類對象組成。
事件監聽器處理流程:
在此以OnClickListener單擊事件為例使用intent來實現頁面的跳轉
監聽事件處理是事件源與事件監聽器分開的而基於回調的事件處理UI組件不但是事件源,而且還是事件監聽器,通過組件的相關回調方法處理對應的事件。
Ⅰ. 自定義View類,繼承自需要的View UI類。ex :自定義 MyButton按鈕類 extends 基礎Button類
Ⅱ. 復寫回調函數。ex:public boolean onTouchEvent(MotionEvent event)
每一個事件回調方法都會返回一個boolean值,①.如果返回true:表示該事件已被處理,不再繼續向外擴散,②.如果返回false:表示事件繼續向外擴散
而說到基於回調就離不開監聽機制 。
幾乎所有基於回調的事件處理方法都有一個boolean類型的返回值,該返回值用於表示該處理方法是否能完全處理該事件。
如果處理事件的回調方法返回true,表明該處理方法已經完全處理改事件,該事件不會傳播出去。
如果處理事件的回調方法返回false,表明該處理方法並未完全處理該事件,該事件會傳播出去。
對於基於回調的時間傳播而言,某組件上所發生的事件不僅會激發該組件上的回調方法,也會觸發該組件所在Activity的回調方法——只要事件能傳播到該Activity。
這里是在模擬器里進行的測試,這里按下鍵盤(而不是點擊),會看到 logcat 中的輸出,如下:
View類實現了KeyEvent.Callback介面中的一系列回調函數,因此,基於回調的事件處理機制通過自定義View來實現,自定義View時重寫這些事件處理方法即可。
Handler是一個消息分發對象。
Handler是Android系統提供的一套用來更新UI的機制,也是一套消息處理機制,可以通過Handler發消息,也可以通過Handler處理消息。
在下面介紹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
發送消息 :
post方法:
send方法:
在子線程中,進行耗時操作,執行完操作後,發送消息,通知主線程更新UI。
本文講解了三個方面;Android事件機制;基於監聽、基於回調以及Handler消息處理。還有許多沒有講解到的知識點,我總結在了整理的一套Android進階筆記裡面;需要學習進階的同學可以前往獲取: Frame Work源碼解析手冊 、 Android核心技術進階手冊、實戰筆記、面試題綱資料
F. Andriod是不是每個線程都有消息隊列
你好
很高興為你解答
答案是:
熟悉Windows編程的朋友可能知道Windows程序是消息驅動的,並且有全局的消息循環系統。而Android應用程序也是消息驅動的,按道 理來說也應該提供消息循環機制。實際上谷歌參考了Windows的消息循環機制,也在Android系統中實現了消息循環機制。Android通過 Looper、Handler來實現消息循環機制,Android消息循環是針對線程的(每個線程都可以有自己的消息隊列和消息循環)。本文深入介紹一下 Android消息處理系統原理。
前面提到Android系統的消息隊列和消息循環都是針對具體線程的,一個線程可以存在(當然也可以不存在)一個消息隊列和一個消息循環 (Looper),特定線程的消息只能分發給本線程,不能進行跨線程,跨進程通訊。但是創建的工作線程默認是沒有消息循環和消息隊列的,如果想讓該線程具 有消息隊列和消息循環,需要在線程中首先調用Looper.prepare()來創建消息隊列,然後調用Looper.loop()進入消息循環。
滿意請採納,謝謝