Android7 Input(十)View 處理Input事件pipeline

概述:

????????本文主要描述View對InputEvent事件pipeline處理過程。

本文涉及的源碼路徑

frameworks/base/core/java/android/view/ViewRootImpl.java

InputEvent事件處理

View處理input事件是調用doProcessInputEvents方法,如下所示:

 void doProcessInputEvents() {// Deliver all pending input events in the queue.while (mPendingInputEventHead != null) {QueuedInputEvent q = mPendingInputEventHead;/* 更新事件處理的位置 */mPendingInputEventHead = q.mNext;/* 處理到了隊列的尾部 */if (mPendingInputEventHead == null) {mPendingInputEventTail = null;}q.mNext = null;......deliverInputEvent(q);}......}

該方法的核心實現邏輯如下:

1、從事件隊列中取出一個待處理的Input事件;

2、調用deliverInputEvent()進行處理;

3、處理完所有事件后,退出循環體;

我們繼續講解deliverInputEvent方法,實現如下:

 private void deliverInputEvent(QueuedInputEvent q) {......InputStage stage;if (q.shouldSendToSynthesizer()) {stage = mSyntheticInputStage;} else {stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;}/* 開啟事件分發 */if (stage != null) {stage.deliver(q);} else {finishInputEvent(q);}}

該方法,根據事件q設置的flag,從InputStage Pipeline的不同起點,開始處理input事件,為了簡化敘述,我們從點擊觸摸屏輸入事件的處理講起。我跳過輸入法相關,流水線的處理起點為mFirstPostImeInputStage,也就是earlyPostImeStage。

InputEvent事件處理Pipeline

承接上文,我們開始將入inputEvent事件的處理Pipeline,前面的文章我們已經講述了InputStage的處理模型,因此不再詳解講述每一個pipeline被調用到的過程,我們只講述每一級中核心方法onProcess。 為了簡化講述的過程,我們跳過與輸入法相關的,流水線起點為earlyPostImeStage

1、EarlyPostImeStage

調用EarlyPostImeInputStage類中的process方法,如下所示:

 protected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q);} else {final int source = q.mEvent.getSource();if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {return processPointerEvent(q);}}return FORWARD;}

我們只關注觸摸事件,因此直接調用processPointerEvent()進行處理,這部分代碼不再展開詳細描述,當EarlyPostImeInputStage處理完inputEvent后,返回FORWARD,表示將該inputEvent事件傳遞給下一級的流水線進行處理,也就是NativePostImeInputStage處理。

2、NativePostImeInputStage

調用NativePostImeInputStage中的onProcess方法,如下所示:

protected int onProcess(QueuedInputEvent q) {if (mInputQueue != null && q.mEvent instanceof KeyEvent) {mInputQueue.sendInputEvent(q.mEvent, q, true, this);return DEFER;}return FORWARD;}

該方法只處理按鍵事件,其他事件直接轉發,事件傳遞給下一級的流水線進行處理,也就是ViewPostImeInputStage

3、ViewPostImeInputStage

調用ViewPostImeInputStage中的onProcess方法,如下所示:

 protected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q);} else {final int source = q.mEvent.getSource();if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {return processPointerEvent(q);} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {return processTrackballEvent(q);} else {return processGenericMotionEvent(q);}}}

我們只關注觸摸事件,直接調用processPointerEvent進行處理,如下所示:

private int processPointerEvent(QueuedInputEvent q) {final MotionEvent event = (MotionEvent)q.mEvent;mAttachInfo.mUnbufferedDispatchRequested = false;final View eventTarget =(event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?mCapturingView : mView;mAttachInfo.mHandlingPointerEvent = true;boolean handled = eventTarget.dispatchPointerEvent(event);maybeUpdatePointerIcon(event);mAttachInfo.mHandlingPointerEvent = false;if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {mUnbufferedInputDispatch = true;if (mConsumeBatchedInputScheduled) {scheduleConsumeBatchedInputImmediately();}}return handled ? FINISH_HANDLED : FORWARD;}

該方法的核心邏輯如下:

1、找到傳遞事件的Viw視圖入口。這里事件在Android UI窗口處理的起點是Activity。我們下一章進行講解,這里先暫時跳過;

2、根據處理結果,如果事件被處理了,返回FINISH_HANDLED,不再向下一級的流水線傳遞,如果沒有處理,則直接轉發到下一級的流水線處理,也就是SyntheticInputStage,為了簡化敘述,我們這里假設input事件被處理了,則apply的對返回結果的處理如下所示:

 protected void apply(QueuedInputEvent q, int result) {if (result == FORWARD) {forward(q);} else if (result == FINISH_HANDLED) {finish(q, true);} else if (result == FINISH_NOT_HANDLED) {finish(q, false);} else {throw new IllegalArgumentException("Invalid result: " + result);}}

由于事件已經被處理,所以直接調用finish,如下所示:

 protected void finish(QueuedInputEvent q, boolean handled) {q.mFlags |= QueuedInputEvent.FLAG_FINISHED;if (handled) {q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;}// 當前InputStage不再處理,直接轉發forward(q);}

finish中標記inputEvent事件被處理標記,然后再次調用forward,最終調用到了onDeliverToNext接口,如下所示:

 protected void onDeliverToNext(QueuedInputEvent q) {if (DEBUG_INPUT_STAGES) {Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);}if (mNext != null) {mNext.deliver(q);} else {finishInputEvent(q);}}

最終調用的是SyntheticInputStage中onDeliverToNext方法,如下所示:

protected void onDeliverToNext(QueuedInputEvent q) {......super.onDeliverToNext(q);}

然后有調用到了父類的onDeliverToNext,也就是InputStage中的onDeliverToNext方法,此時流水線已經到達了末端,然后進入到finishInputEvent方法如下所示:

private void finishInputEvent(QueuedInputEvent q) {Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",q.mEvent.getSequenceNumber());if (q.mReceiver != null) {boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;q.mReceiver.finishInputEvent(q.mEvent, handled);} else {q.mEvent.recycleIfNeededAfterDispatch();}recycleQueuedInputEvent(q);}

核心邏輯就是調用finishInputEvent進行收尾工作,方法如下所示:

 public final void finishInputEvent(InputEvent event, boolean handled) {......} else {int index = mSeqMap.indexOfKey(event.getSequenceNumber());if (index < 0) {Log.w(TAG, "Attempted to finish an input event that is not in progress.");} else {int seq = mSeqMap.valueAt(index);mSeqMap.removeAt(index);nativeFinishInputEvent(mReceiverPtr, seq, handled);}}event.recycleIfNeededAfterDispatch();}

核心就是調用JNI層中的nativeFinishInputEvent方法,如下所示:

static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,jint seq, jboolean handled) {sp<NativeInputEventReceiver> receiver =reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);status_t status = receiver->finishInputEvent(seq, handled);if (status && status != DEAD_OBJECT) {String8 message;message.appendFormat("Failed to finish input event.  status=%d", status);jniThrowRuntimeException(env, message.string());}
}

繼續調用finishInputEvent方法,我們進一步追蹤,最終調用到了sendUnchainedFinishedSignal這個方法,如下所示:

status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {InputMessage msg;msg.header.type = InputMessage::TYPE_FINISHED;msg.body.finished.seq = seq;msg.body.finished.handled = handled;return mChannel->sendMessage(&msg);
}

????????這里又看到了熟悉的InputChannel的東西,最后將結束處理的信息通過夸進程通知到了InputDispatcher;然后觸發epoll事件,調用到handleReceiveCallback方法進行處理完事件的清理工作,這里不再展開描述;

總結

????????本文描述了Android系統中View對輸入事件的處理流程,也就是pipeline事件的處理過程。然后最終將處理結果通過跨進程傳遞給了InputDispatcher。下一章,我們講述View UI的事件分發流程;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/83654.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83654.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/83654.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Neo4j 完全指南:從入門到精通

第1章&#xff1a;Neo4j簡介與圖數據庫基礎 1.1 圖數據庫概述 傳統關系型數據庫與圖數據庫的對比圖數據庫的核心優勢圖數據庫的應用場景 1.2 Neo4j的發展歷史 Neo4j的起源與演進Neo4j的版本迭代Neo4j在圖數據庫領域的地位 1.3 圖數據庫的基本概念 節點(Node)與關系(Relat…

網心云 OEC/OECT 筆記(1) 拆機刷入Armbian固件

目錄 網心云 OEC/OECT 筆記(1) 拆機刷入Armbian固件網心云 OEC/OECT 筆記(2) 運行RKNN程序 外觀 內部 PCB正面 PCB背面 PCB背面 RK3566 1Gbps PHY 配置 OEC 和 OECT(OEC-turbo) 都是基于瑞芯微 RK3566/RK3568 的網絡盒子, 沒有HDMI輸入輸出. 硬件上 OEC 和 OECT…

攝像機ISP處理流程

1.Bayer&#xff1a;生成raw圖&#xff0c;添加色彩數據&#xff08;RGB&#xff09;&#xff0c;一般會將G的占比設置為R和B的和&#xff0c;實例&#xff1a; 2.黑電平矯正&#xff1a;減去暗電流造成的誤差&#xff1b; 3.鏡頭矯正&#xff1a;對四周的亮度進行矯正&#x…

【后端架構師的發展路線】

后端架構師的發展路線是從基礎開發到技術領導的系統性進階過程&#xff0c;需融合技術深度、架構思維和業務洞察力。以下是基于行業實踐的職業發展路徑和關鍵能力模型&#xff1a; 一、職業發展階梯? 初級工程師&#xff08;1-3年&#xff09;? 核心能力?&#xff1a;掌…

Unity VR/MR開發-VR開發與傳統3D開發的差異

視頻講解鏈接&#xff1a;【XR馬斯維】VR/MR開發與傳統3D開發的差異【UnityVR/MR開發教程--入門】_嗶哩嗶哩_bilibili

RabbitMQ如何保證消息可靠性

RabbitMQ是一個流行的開源消息代理&#xff0c;它提供了可靠的消息傳遞機制&#xff0c;廣泛應用于分布式系統和微服務架構中。在現代應用中&#xff0c;確保消息的可靠性至關重要&#xff0c;以防止消息丟失和重復處理。本文將詳細探討RabbitMQ如何通過多種機制保證消息的可靠…

批量圖片管理軟件介紹

軟件介紹 本文介紹一款功能全面的圖片處理軟件 - FastStone Image Viewer。 軟件功能概述 FastStone Image Viewer不僅支持圖片查看&#xff0c;還具備編輯、批量重命名和批量轉換等多種實用功能。 用戶授權說明 該軟件對個人用戶完全免費&#xff0c;企業用戶只需輸入用戶…

Playwright 測試框架 - Java

??【Playwright + Java 實戰教程】從零到一掌握自動化測試利器! ?? 本文專為 Java 開發者量身打造,通過詳盡示例帶你快速掌握 Playwright 自動化測試。涵蓋基礎操作、表單交互、測試框架集成、高階功能及常見實戰技巧,適用于企業 UI 測試與 CI/CD 場景。 ??? 一、環境…

nvidia系列教程-Usb otg模式修改為host模式

目錄 前言 一、了解 USB OTG 模式與 Host 模式 二、host模式切換 總結 前言 在 NVIDIA 設備的使用過程中,有時我們需要將 USB OTG(On-The-Go)模式切換為 Host 模式,以滿足連接外部設備(如 U 盤、鼠標、鍵盤等)的需求。本文將詳細介紹如何在 NVIDIA 設備上進行這一模式…

二叉樹-104.二叉樹的最大深度-力扣(LeetCode)

一、題目解析 這里需要注意根節點的深度是1&#xff0c;也就是說計算深度的是從1開始計算的 二、算法原理 解法1&#xff1a;廣度搜索&#xff0c;使用隊列 解法2&#xff1a;深度搜索&#xff0c;使用遞歸 當計算出左子樹的深度l&#xff0c;與右子樹的深度r時&#xff0c;…

Calendar類日期設置進位問題

背景 報表需求&#xff0c;需要傳遞每組數據中最小的日期&#xff0c;后臺根據傳遞的最小日期&#xff0c;向前取參數傳遞的月份的上個月為結束時間的近五個月數據 例&#xff1a;參數傳:2025/02&#xff0c;則需返回2025/01, 2024/12, 2024/11, 2024/10, 2024/09這五個年月數據…

編程筆記---問題小計

編程筆記 qml ProgressBar 為什么valuemodel.progress / 100 在QML中&#xff0c;ProgressBar的value屬性用于表示進度條的當前進度值&#xff0c;其范圍通常為0到1&#xff08;或0%到100%&#xff09;。當使用model.progress / 100來設置value時&#xff0c;這樣做的原因是為…

【STL】函數對象+常用算法

文章目錄 STL- 函數對象函數對象函數對象使用 謂詞一元謂詞二元謂詞內建函數對象算術仿函數關系仿函數 STL- 常用算法常用遍歷算法for_eachtransform 常用查找算法findfind_ifadjacent_findbinary_searchcountcount_if 常用排序算法sortrandom_shufflemergereverse 常用拷貝和替…

[JVM] JVM內存調優

&#x1f338;個人主頁:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;?熱門專欄: &#x1f9ca; Java基本語法(97平均質量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection與…

Spring Boot 從Socket 到Netty網絡編程(下):Netty基本開發與改進【心跳、粘包與拆包、閑置連接】

上一篇&#xff1a;《Spring Boot 從Socket 到Netty網絡編程&#xff08;上&#xff09;&#xff1a;SOCKET 基本開發&#xff08;BIO&#xff09;與改進(NIO)》 前言 前文中我們簡單介紹了基于Socket的BIO&#xff08;阻塞式&#xff09;與NIO&#xff08;非阻塞式&#xff0…

python編寫賽博朋克風格天氣查詢程序

工具介紹 這個天氣查詢工具是一個基于 Python 的桌面應用程序,使用了tkinter庫來創建圖形用戶界面(GUI),并通過requests庫調用 Open - Meteo API 獲取天氣數據。它具有賽博朋克風格的界面設計,提供了當前天氣信息、15 天天氣預報以及詳細的天氣數據展示,同時還包含溫度趨…

從二叉樹到 STL:揭開 set 容器的本質與用法

前言&#xff1a; 上次介紹完二叉搜索樹后&#xff0c;更新中斷了一段時間&#xff0c;先向大家致歉。最近學習狀態有些起伏&#xff0c;但我正在努力調整&#xff0c;相信很快會恢復節奏。今天我們繼續深入探討——關聯容器&#xff0c;它在算法和工程中都非常常見和重要。 1…

uv管理spaCy語言模型

本文記錄如何在使用uv管理python項目dependencies時&#xff0c;把spaCy的模型也納入其中. spaCy 一、spaCy簡介 spaCy是一個開源的自然語言處理&#xff08;NLP&#xff09;庫&#xff0c;它主要用于處理文本數據。它支持多種語言&#xff0c;包括英語、中文等。它是由Expl…

python執行測試用例,allure報亂碼且未成功生成報告

allure執行測試用例時顯示亂碼&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;?&#xfffd;&#xfffd;&#xfffd;&#xfffd;?&#xfffd;&#xfffd;&#xfffd;??&#xfffd;&#xfffd;&#xfffd;?&#xfffd;&am…

Rust 學習筆記:Box<T>

Rust 學習筆記&#xff1a;Box Rust 學習筆記&#xff1a;Box<T\>Box\<T> 簡介使用 Box\<T\> 在堆上存儲數據啟用帶有 box 的遞歸類型關于 cons 列表的介紹計算非遞歸類型的大小使用 Box\<T\> 獲取大小已知的遞歸類型 Rust 學習筆記&#xff1a;Box<…