Android 觸控訊息處理
1. WindowInputEventReceiver.onInputEvent() ----ViewRootImpl.java
從InputDispatch中publish一個Eent事件後,會由WindowInputEventReceiver.onInputEvent作為回撥函式被呼叫。
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
1.2. ViewRootImpl.enqueueInputEvent()
把event作為一個QueuedInputEvent放到列表佇列最後,如果processImmediately == true那麼就直接執行doProcessInputEvents()去立即處理這個event事件,如果不是就呼叫scheduleProcessInputEvents把Eent通過hander放入到主執行緒的Looper中。
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp. // We do this because the application or the IME may inject key events // in response to touch events and we want to ensure that the injected keys // are processed in the order they were received and we cannot trust that // the time stamp of injected events are monotonic. QueuedInputEvent last = mFirstPendingInputEvent; if (last == null) { mFirstPendingInputEvent = q; } else { while (last.mNext != null) { last = last.mNext; } last.mNext = q; } if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } }
2. ViewRootImpl.deliverInputEvent()
最後都會呼叫deliverInputEvent去分發事件,如果是KeyEent就呼叫deliverKeyEent(),同理如果是Touch,Pointer Event就呼叫deliverPointerEvent。
private void deliverInputEvent(QueuedInputEvent q) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent"); try { if (q.mEvent instanceof KeyEvent) { deliverKeyEvent(q); } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { deliverPointerEvent(q); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { deliverTrackballEvent(q); } else { deliverGenericMotionEvent(q); } } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
2.1 ViewRootImpl.deliverPointerEvent()
1. 如果mView == null || !mAdded, 就直接呼叫fininshInputEvent去告訴InputDispatcher;
2. 如果是ActionDown就是通過ensureTouchMode(true)告訴WMS去設定對應WindowState的touch mode,並且呼叫ensureTouchModeLocally來handle the change
3. 如果是touchevent就是用mLastTouchPoint去記錄此次的Point的Position用於possible drag-initiation
4. mView.dispatchPointerEvent(event); 通過DecorView去dispatchPointerEvent;
5. 無論Event是否被處理,都會呼叫finishInputEvent(q, true);去告訴InputDispatcher。
private void deliverPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
final boolean isTouchEvent = event.isTouchEvent();
if (mInputEventConsistencyVerifier != null) {
if (isTouchEvent) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
} else {
mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
}
}
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishInputEvent(q, false);
return;
}
// Translate the pointer event for compatibility, if needed.
if (mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
}
// Enter touch mode on down or scroll.
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
ensureTouchMode(true);
}
// Offset the scroll position.
if (mCurScrollY != 0) {
event.offsetLocation(0, mCurScrollY);
}
if (MEASURE_LATENCY) {
lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
}
// Remember the touch position for possible drag-initiation.
if (isTouchEvent) {
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
}
// Dispatch touch to view hierarchy.
boolean handled = mView.dispatchPointerEvent(event);
if (MEASURE_LATENCY) {
lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
}
if (handled) {
finishInputEvent(q, true);
return;
}
// Pointer event was unhandled.
finishInputEvent(q, false);
}
2.1.4 DecorView.dispatchPointerEvent
DecorView繼承FrameLayout也就間接繼承了ViewGroup,View。
1. 呼叫父類View的dispatchPointerEvent(), 然後去呼叫自己的dispatchTouchEvent;
2. dispatchTouchEvent中呼叫callback.dispatchTouchEvent,這裡的Callback就是Activity物件。
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
2.1.4.2 Activity.dispatchtouchEvent
1. onUserInteraction(), 在dispatch之前做一些操作
2. getwindow().superDispatchtouchEvent就是呼叫PhoneWindow中的superDispatchTouchEvent。而PhoneWindow也是直接呼叫mDecorView的對應的方法。而DoverView的superDispatchTouchEvent方法中是去呼叫了父類ViewGroup的dispatchTouchEvent。
3. 當Activity中所有的View都不處理Event的時候,就用由Activity的onTouchEvent()來處理。
這裡的呼叫順序從DoverView---->Activity-->PhoneWindow--->DocerView---->ViewGroup
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
2.1.4.2.1 ViewGroupdispatchTouchEvent(MotionEvent ev)
1. 如果是Action_Down事件,那麼把之前的TouchTargets和TouchState都clear掉,mFirstTouchTarget = null
2. onInterceptTouchEvent(),通過這個函式去告訴當前的View是否攔截掉這個Event,如果return就不會把這個event往下dispatch了
3. 如果不去Intercept當前的Event,就通過遍歷自己的child views去找到處在Touch所在區域的view,找到之後通過getTouchTarget(View)去查詢View是否在TouchTarget中了;如果不在,則呼叫dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign),把Event分發給child view。
4. 如果child沒有消耗掉event事件,那麼mFirstTouchTarget == null),這時候就會呼叫handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)自己去處理此次Event; 引數中child為null。
如果ViewGroup也沒處理,就會以此回溯上去給父View處理。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
... ...
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
for (int i = childrenCount - 1; i >= 0; i--) {
final View child = children[i];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
newTouchTarget = getTouchTarget(child); //通過getTouchTarget去查詢View是否在TouchTarget中了。
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = i;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
2.1.4.2.1.3 ViewGroup.dispatchTransformedTouchEvent()
1. 主要是呼叫child.dispatchTouchEvent(transformedEvent);把事件遞迴傳下去,如果child還是一個ViewGroup那麼步驟和上面有一樣,如果是View就呼叫View.dispatchTouchEvent(MotionEvent event)
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
2.1.4.2.1.3.1View.dispatchTouchEvent(MotionEvent event)
1. 如果有TouchListener就去呼叫註冊過的TouchListener的回撥函式onTouch事件並直接返回。這就是為什麼我們想接收一個touch event的時候只要寫一個listener的原故。
2. 如果沒有TouchListener,則呼叫預設的onTouchEvent(event)事件
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}
2.1.4.2.1.4 Activity.onTouchEvent(MotionEvent event)
如果所有的View都不處理TouchEvent,最後由Activity來處理。
判斷一下mWindow是否因此此Event要close掉,如果不close,就return false。
最後呼叫FinishInputEvent(false), 返回這個沒有處理的Event。
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
相關推薦
Android 觸控訊息處理
1. WindowInputEventReceiver.onInputEvent() ----ViewRootImpl.java 從InputDispatch中publish一個Eent事件後,會由WindowInputEventReceiver.onInpu
android 非同步訊息處理機制 — AHandler
1. 引入 ALooper、AHandler、AMessage 在 android multimedia stagefright 的框架程式碼中,通篇都是這幾個類的身影,所以熟悉 android 多媒體框架的第一步必須理解這幾個類的含義。 這幾個類是為了實現非同步訊息機制而設計的
Android非同步訊息處理機制詳解及原始碼分析
PS一句:最終還是選擇CSDN來整理髮表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支援MarkDown語法了,牛逼啊! 【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】 最近相對來說比較閒,加上養病,所
Android之訊息處理機制(二)Handler的本質-Message和Looper到底是什麼?
目錄 Android之訊息處理機制(二) 以下皆為乾貨,比較幹,需要讀者細細理解。 前面(一)已經解釋了Handler的基本機制了,下面來概括一下本質。 一、MessageQueue MessageQueue其實就
Android非同步訊息處理機制:Looper、Handler、Message
1 簡介 Handler,Looper,Message這三者都與Android非同步訊息處理執行緒相關, Looper:負責建立一個MessageQueue,然後進入一個無限迴圈體不斷從該MessageQueue中讀取訊息; Handler:訊息建立者,一個或者多個
淺談Android的訊息處理機制--Handler
1.為什麼有Handler? 在UI執行緒中不能進行耗時操作,例如資料讀寫、網路請求、圖片載入等,所以這些操作被放在子執行緒裡,Handler便是子執行緒和UI執行緒之間通訊的橋樑之一。 2.幹什麼用的? 進行非同步訊息處理,即上述內容。 3.Handler類裡面有什麼是必須知道
android的訊息處理機制——Looper,Handler,Message (原理圖、原始碼)
轉自:http://my.oschina.net/u/1391648/blog/282892 在開始討論android的訊息處理機制前,先來談談一些基本相關的術語。 通訊的同步(Synchronous):指向客戶端傳送請求後,必須要在服務端有迴應後客戶端才繼續傳送
Android 按鍵訊息處理
Android按鍵訊息處理 在android系統中,鍵盤按鍵事件是由SystemServer服務來管理的;然後在以訊息的形式分發給應用程式處理。產生鍵盤按鍵事件則是有Linux kernel的相關驅動來實現。 鍵盤訊息有別於其他型別的訊息;需要從Linux kernel drivers產生由上層ap
Android按鍵訊息處理
Android按鍵訊息處理 在android系統中,鍵盤按鍵事件是由SystemServer服務來管理的;然後在以訊息的形式分發給應用程式處理。產生鍵盤按鍵事件則是有Linux
Android的訊息處理機制:Message、Handlerhe和Looper原始碼解析
android的訊息處理有三個核心類:Looper,Handler和Message。其實還有一個Message Queue(訊息佇列),但是MQ被封裝到Looper裡面了,我們不會直接與MQ打交道,因此我沒將其作為核心類。下面一一介紹: 執行緒的魔法師 Looper Loo
Android 非同步訊息處理
前言 我們都知道Android的UI執行緒是不安全,在子執行緒中更新UI會造成程式崩潰,但是我們有時候確實需要在子執行緒中進行耗時操作,然後再依據結果更新UI 在學習具體的方法前我們首先看一下Android的非同步訊息處理機制示意圖 我們再來把
(轉載)Android 非同步訊息處理機制 讓你深入理解 Looper、Handler、Message三者關係
很多人面試肯定都被問到過,請問Android中的Looper , Handler , Message有什麼關係?本篇部落格目的首先為大家從原始碼角度介紹3者關係,然後給出一個容易記憶的結論。 1、 概述 Handler 、 Looper 、Message
深入理解Android非同步訊息處理機制
一。概述 Android 中的非同步訊息處理主要分為四個部分組成,Message、Hndler、MessageQueue 和 Looper。其關係如下圖所示: 1. Message 是執行緒之間傳遞的訊息,它可以在內部攜帶少量資訊,用於在不同執行緒之間交換資料。 2. Messag
android非同步訊息處理機制
android非同步訊息處理主要由四部分組成:Handler,Looper,Message,MessageQueue Message:執行緒之間傳遞的訊息,可以在內部攜帶少量訊息 MessageQueue: Looper:每個執行緒有且最多隻能有一個Looper物件
Android非同步訊息處理機制的原始碼分析
1、背景 相信做過一段時間的Android開發都瞭解,當我們在子執行緒中更新UI時會丟擲異常,導致程式崩潰,Android4.0之後只允許在UI執行緒中更新介面,但是我們也不能再UI執行緒中處理耗時操作,那樣會導致應用程式無響應(即出現ANR)。 那如果想解
Android非同步訊息處理機制 handler
我們都知道,Android UI是執行緒不安全的,如果在子執行緒中嘗試進行UI操作,程式就有可能會崩潰。相信大家在日常的工作當中都會經常遇到這個問題,解決的方案應該也是早已爛熟於心,即建立一個Message物件,然後藉助Handler傳送出去,之後在Handler的han
Android非同步訊息處理機制原始碼分析
宣告:本文是參考了以下幾位大神的文章,自己按照自己的思維習慣整理的筆記,並新增一些相關的內容。如有不正確的地方歡迎留言指出,謝謝! 郭霖部落格 鴻洋部落格 任玉剛《Android開發藝術探索》 一. Andoid訊息機制概述
Android之訊息處理機制
Android應用程式有兩大機制:訊息機制和事件處理機制今天我們來簡單談下訊息機制一、基本元素Looper:訊息管理器,管理Message Queue。Message:訊息Message Queue:訊息佇列。Looper會不斷地從MessageQueue中去取訊息進行傳送到
Android 非同步訊息處理機制解析
一、Message、Handler、MessageQueue、Looper Android 中的非同步訊息處理主要由四個部分組成,Message、Handler、MessageQueue、Looper。 1. Message: Message 是線上