當前位置:首頁 » 安卓系統 » Android事件的分發機制

Android事件的分發機制

發布時間: 2023-10-07 14:15:59

1. 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 類:

2. android事件分發機制 什麼意思

android事件分發機制 就是一個觸摸事件發生了,從一個窗口傳遞到一個視圖,再傳遞到另外一個視圖,最後被消費的過程,在android中還是比較復雜的傳遞流程如下:

(1) 事件從Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的View(ViewGroup)開始一直往下(子View)傳遞。子View可以通過onTouchEvent()對事件進行處理。

(2) 事件由父View(ViewGroup)傳遞給子View,ViewGroup可以通過onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。

(3) 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子View沒有消費事件,事件會反嚮往上傳遞,這時父View(ViewGroup)可以進行消費,如果還是沒有被消費的話,最後會到Activity的onTouchEvent()函數。

(4) 如果View沒有對ACTION_DOWN進行消費,之後的其他事件不會傳遞過來。

(5) OnTouchListener優先於onTouchEvent()對事件進行消費。

上面的消費即表示相應函數返回值為true。

3. Android-View的事件分發及攔截-父控制項和子控制項都處理觸摸事件的方式

比如接著上篇 Android-View的事件分發及攔截機制簡單流程先體驗再研究(場景?疑問? 具體?待續...) ,小白現在要實現就是子View和父ViewGroup都響應點擊事件。

1. 單純的都只是響應down事件

這個就很簡單了 - 直接子View的**public boolean **onTouchEvent(MotionEvent event) 裡面直接返回false就行了。也就是子控制項響應了一次down後,接下來就交給父ViewGroup了.(子View就啥幾把也幹不了了);

2. 響應down和up事件,move啥的

我們知道子View如果onTouch裡面返回了true,那麼將會處理後續的move,up事件。而不再交給上層父ViewGroup。那父ViewGroup就沒辦法在Touch裡面處理,所以我們只能放到dispatchTouchEvent或者onInterceptTouchEvent中處理這個down,up等事件:

比如dispatchTouchEvent中:

這樣的情況就是父ViewGroup先執行點擊事件,然後子View再執行。 如果您需要父ViewGroup晚點,可以延時執行啥的。

如果此時,子View的dispatchTouchEvent返回true - 表示攔截,不繼續了

那麼子View的所有的事件都不會響應了。其實也就是我們的一個事件先傳遞,touch再處理的樹形圖:

網路上拔個圖來

簡單記錄下下而已,繼續加深理解...這是上一篇的續,還是上一篇....嘖嘖....後面是官方文檔分析來著...

4. 【Android】ANR是如何產生的

眾所周知,Android的輸入事件是通過 InputReader 監聽系統 dev/input 下的文件來獲取輸入事件,並由 InputDispatcher 來進行分發的。

而ANR事件就是在 InputDispatcher 中產生的。

InputDispatcher 內部維護了一個線程 InputDispatcherThread ,輸入事件在這個線程中進行處理。這個線程在 InputManager 中進行創建和啟動。

它只做了一件事,就是無限調用 dispatchOnce() 進行事件分發。

dispatchOnce() 會調用 dispatchOnceInnerLocked() 進行事件分發,而如果判斷出當前事件是觸摸事件,則會又調用 dispatchMotionLocked() 來分發觸摸事件。在處理結束之後,會阻塞直到下一次事件的到來。

dispatchMotionLocked() 會調用 () 查找觸摸事件對應窗口目標並進行分發。如果當前窗口尚有未處理完的事件,則會調用 handleTargetsNotReadyLocked 處理。

handleTargetsNotReadyLocked 會判斷目標事件等待時間,如果其大於5秒,則會調用 onANRLocked 進入ANR流程。

以上便是ANR的產生過程。

ANR的產生有兩個必要條件:

/frameworks/native/services/inputflinger/InputDispatcher.cpp
Android輸入事件分發與攔截

5. Android Touch事件分發處理機制詳解

Android應用的開發過程不可能不涉及到Touch事件的處理,簡單地如設置OnClickListener、OnLongClickListener等監聽器處理View的點擊事件,復雜地如在自定義View中通過重寫onTouchEvent來捕獲用戶交互事件以定製出各種效果,在使用的過程中或多或少會遇到一些奇怪的Bug,讓你對Touch事件「從哪來,到哪去」產生迷之疑惑,經過多少次徘徊之後終於決定系統的分析下源碼,本文就給大家分享下我的收獲。

MotionEvent作為Touch事件的載體,採用時間片來管理Touch事件所有相關行為的數據,本文這樣理解時間片這個概念:

通常MotionEvent會將觸發當前事件的Pointer作為主要Pointer,其PointerIndex為0,而MotionEvent通過提供getX()這類不帶index參數的介面以更方便的操作主要Pointer的數據。
了解了MotionEvent的組成結構之後,接下來就可以分析MotionEvent包含的事件類型了,MotionEvent通過getAction介面來獲取事件Action,而Action中低8位地址存儲的是事件類型(對於觸摸事件來說,主要包括Down、Move、Up、Cancel、PointerDown、PointerUp),高8位地址存儲的是PointerId(當事件類型為PointerDown、PointerUp時)。通常來說事件會以Down開始,以Up或Cancel結束,各事件所承擔的角色以及各自的特點在分析事件分發與處理的過程時再詳細說明。
另外,MotionEvent中的Flag需要說明一下:

本文僅分析Touch事件在Framework中Java層的傳遞,因此從事件傳遞到Activity開始分析。當Touch事件傳遞給Activity時,會調用Activity.dispatchTouchEvent(MotionEvent),Activity會將事件傳遞給其Window進行處理,實際會調用PhoneWindow.superDispatchTouchEvent(MotionEvent),PhoneWindow會將該事件傳遞給Android中View層級前埋中的頂層View(即DecorView)進行處理:

在Window未設置Callback的情況下,會調用父類的dispatchTouchEvent,DecorView繼承自FrameLayout,然後FrameLayout並未實現dispatchEvent,因此最終調用ViewGroup.dispatchTouchEvent,也就是Touch事件分發的核心邏輯所在,前文中提到MotionEvent中事件類型主要包括Down、Move、Up、Cancel、PointerDown、PointerUp,而dispatchTouchEvent根據事件的不同類型會做不同處理,因此這里分別進行分析:

Down事件處理

非異常情況下,Touch事件的事件周期總是以Down事件開始的,因此Down事件在整個事件分發邏輯中起關鍵作用,將決定了後續Move、Up及Cancel事件的處理主體,先看一張Down事件分發的流程圖:

從流程圖中可以看到,Down事件的分發邏輯主要目的在於尋找到能處理該Touch事件的View控制項(該View為以當前ViewGroup為Root節點的View層級中的View,利用尋找到的View創建事件處理Target),整個處理邏輯主要包含以下渣悔陸幾步:

Move、Up、Cancel事件處理

完成Down事件的分發邏輯後,就確定了該Down事件後續Move、Up及Cancel事件的處理主體(注意:這里並沒有確定PointerDown事件的處理主體,關於PointerDown事件的分發邏輯稍後分析),先通過一張流程圖來感受下Move、Up、Cancel事件的分發邏輯:

從流程圖可以看出,對於Move、Up、Cancel事件的分發步驟如如頃下:

PointerDown事件處理

PointerDown事件是在支持多Pointer(調用將FLAG_SPLIT_MOTION_EVENTS置位)的環境下,當有新的Pointer按下時產生的,該事件處理的特殊性在於會重新遍歷View層級,尋找可以處理新Pointer事件的Target,具體流程參考Down事件的分發邏輯;遍歷結束若仍沒有找到處理該事件的Target,則會將新Pointer的處理權設置給已有Target中最早被添加的Target。完成Target的尋找之後,會將該事件通過dispatchTransformedTouchEvent傳遞至所有已有Target進行處理,可以通過下面流程圖,對PointerDown事件的處理有一個更全局的認識:

PointerUp事件處理

相對於Up事件來說,對於PointerUp事件的處理區別在於當傳遞至所有已有Target結束之後並不能標記以Down事件起始的整個事件周期結束,僅能標記其關聯Pointer(以PointerDown事件起始)的事件周期結束,因此不會清除所有狀態,而僅會從已有Target中移除掉與該Pointer相關的部分。

onInterceptTouchEvent

在ViewGroup進行事件分發的過程中,會調用該函數來確定是否需要攔截事件,當該函數返回true時該事件將會被攔截,即不會進行正常的View層級傳遞,而是直接由該ViewGroup來處理,而攔截後的操作需要根據攔截事件的類型不同而不同:

dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)

在將事件傳遞給Target進行處理之前會調用該函數對MotionEvent進行處理:

MotionEvent.split(int idBits)

判斷一個View控制項是否消費一個事件,是由View.dispatchEvent的返回值來決定的,而View.dispatchEvent用於尋找事件的最終消費者,話不多說,還是通過一張流程圖來個直觀感受:

從流程圖中可以看出,View會根據ouch事件對Scroll狀態進行調整,並尋找該事件的最終處理器:

View.dispatchEvent將向其直接ViewGroup返回是否消費掉該事件,返回值將決定上級ViewGroup是否需要繼續詢問其他子View是否需要消費該事件。這就是View中分發事件的邏輯,真是簡單粗暴!

從View.dispatchEvent的分析中可以發現當未對View設置mTouchListener或mTouchListener未消費掉該事件時,Touch事件最終將由View.onTouchEvent來決定是否消費,自定義View可以重寫該方法實現自身的邏輯,此處僅分析View中的通用處理邏輯:

從上述分析可以很開心地發現熟悉的onClick及onLongClick事件的產生邏輯,若是之前沒看過類似的文章,應該會有原來如此的感覺吧,哈哈~~

至此,Touch事件的分發與處理流程算是走通了,個人看完整個源碼之後有種豁然開朗的感覺,能很清晰的分析向「為什麼事件有時候傳到某個View有時候卻不傳?」、「有時候只傳前面幾個事件後面卻不傳了?」等問題,也希望本文的分析能讓你更清晰地感知Android中Touch事件的傳遞流程,如果發現文中有何錯誤,希望不吝賜教!

6. 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;

}

7. Android事件分發機制

Android中對視圖的Touch事件進行分發處理。
單手指操作:ACTION_DOWN -> ACTION_MOVE -> ACTION_UP
多手指操作:ACTION_DOWN -> ACTION_POINTER_DOWN -> ACTION_MOVE -> ACTION_POINTER_UP -> ACTION_UP.

(1) dispatchTouchEvent() :事件分發

(2) onInterceptTouchEvent() :事件攔截

(3) onTouchEvent() :事件處理

ViewGroup 的相關事件有三個:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。

View 的相關事件只有兩個:dispatchTouchEvent、onTouchEvent。

先分析ViewGroup的處理流程:首先得有個結構模型概念:ViewGroup和View組成了一棵樹形結構,最頂層為Activity的ViewGroup,下面有若乾的ViewGroup節點,每個節點之下又有若乾的ViewGroup節點或者View節點,依次類推。如圖:

點擊事件達到頂級 View(一般是一個 ViewGroup),會調用 ViewGroup 的 dispatchTouchEvent 方法,如果頂級 ViewGroup 攔截事件即 onInterceptTouchEvent 返回 true,則事件由 ViewGroup 處理,這時如果 ViewGroup 的 mOnTouchListener 被設置,則 onTouch 會被調用,否則 onTouchEvent 會被調用。也就是說如果都提供的話,onTouch 會屏蔽掉 onTouchEvent。在 onTouchEvent 中,如果設置了 mOnClickListenser,則 onClick 會被調用。如果頂級 ViewGroup 不攔截事件,則事件會傳遞給它所在的點擊事件鏈上的子 View,這時子 View 的 dispatchTouchEvent 會被調用。如此循環。

熱點內容
安卓手機軟體用什麼編程語言寫 發布:2024-05-06 14:30:07 瀏覽:657
des解密python 發布:2024-05-06 14:30:06 瀏覽:684
n的階乘演算法 發布:2024-05-06 14:29:57 瀏覽:552
安卓手機為什麼停服 發布:2024-05-06 14:29:08 瀏覽:93
電腦伺服器不運行是怎麼回事 發布:2024-05-06 14:20:28 瀏覽:791
肥皂板解壓視頻大全 發布:2024-05-06 14:20:27 瀏覽:260
ps4各個伺服器有什麼區別 發布:2024-05-06 14:10:38 瀏覽:485
手機上怎麼玩韓國伺服器游戲 發布:2024-05-06 14:10:20 瀏覽:59
頻繁的解壓縮 發布:2024-05-06 14:09:30 瀏覽:821
怎麼在紅帽上裝c語言編譯器 發布:2024-05-06 13:58:38 瀏覽:508