從源碼解釋Android事件分發機制

來源:互聯網
上載者:User

從源碼解釋Android事件分發機制

在ViewRootImpl的setView方法中,使用者的觸摸按鍵訊息是體現在表單上的,而windowManagerService則是管理這些視窗,它一旦接收到使用者對表單的一些觸摸按鍵訊息,會進行相應的動作,這種動作是需要體現在具體的view上面,在Android中,一個具體的介面是由一個Activity呈現的,而Activity中則包含了一個window,此window中又包含了一個phoneWindow,這個phoneWindow才是真正意義上的視窗,它把一個架構布局進行了一定的封裝,並提供了具體的視窗操作介面,phoneWindow中包含了一個DecorView,這個view才是包含整個Activity的ui,它將被attach到Activity主視窗中。所以說使用者觸摸按鍵的訊息是由windowManagerService捕捉到然後交給phoneWindow中的DecorView進行相應的處理,而串連兩者的橋樑則是一個ViewRoot類,ViewRoot類由windowManagerService建立,其內部有一個W類,這個W類是一個binder,負責WindowManagerService的ipc調用,W接收到windowManagerService發送過來的訊息後,把訊息傳遞給ViewRoot,進而傳遞給ActivityThread解析做出處理,

1、在ViewRootImpl.java類的setView方法中:

 

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mInputChannel);
在這裡把mWindow傳遞給了window,這個mWindow就是W類的一個執行個體 2、當具體的觸摸按鍵訊息發生後,會由ViewRootImpl類中WindowInputEventReceiver這個類的onInputEvent方法負責接收,其實也就是個回調。
public void onInputEvent(InputEvent event) {        enqueueInputEvent(event, this, 0, true); }
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();        }    }

在enqueueInputEvent方法中,把event加入到隊列的最後面,如果processImmediately為true,則直接調用doProcessInputEvents方法,否則scheduleProcessInputEvents被調用,這兩個方法最後都調用了deliverInputEvent方法,用於分發輸入事件。在這個方法中判斷如果是按鍵事件,比如說back、home等,就會調用deliverKeyEvent分發事件,一些一般的移動事件調用deliverGenericMotionEvent方法。
 private void deliverInputEvent(QueuedInputEvent q) {        Trace.traceBegin(Trace.TRACE_TAG_VIEW, deliverInputEvent);        try {            if (q.mEvent instanceof KeyEvent) {// 如果是按鍵事件,也就是back、home等按鍵                deliverKeyEvent(q);            } else {            // touch事件                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);        }    }
在deliverPointerEvent方法中,如果view對象不存在,或者沒有被添加,則這個event不會被處理,直接finishInputEvent。接著判斷action是否是MotionEvent.ACTION_DOWN,如果是,則表示觸摸方式改變了,需要告訴windowManager在本地進行處理,因為每一次的按下操作都代表了一個新的event事件的到來。然後記錄觸摸的位置,這個位置就代表了應該是哪一個view來接收這個事件,然後直接調用mView.dispatchPointerEvent(event)來分發這個事件,如果這個事件被分發下去了,則結束事件。
  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) {// view對象為空白,或者沒有被添加,這個事件就不會被處理            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) {// 如果是MotionEvent.ACTION_DOWN// 如果是如果觸摸方式改變,告訴wm,在本地進行處理// 每一次的按下操作就是一個觸摸事件的改變            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.        // 給view的層級上view分發事件        //  DecorView繼承FrameLayout也就間接繼承了ViewGroup,View        // DoverView---->Activity-->PhoneWindow--->DocerView---->ViewGroup        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);    
在DecorView中沒有dispatchPointerEvent方法,所以調用的是View.java的dispatchPointerEvent方法中判斷具體的是哪一類的事件,然後又調用了自身的dispatchTouchEvent。
public final boolean dispatchPointerEvent(MotionEvent event) {        if (event.isTouchEvent()) {// 如果是觸摸事件            return dispatchTouchEvent(event);        } else {        // 如果是一般的移動事件            return dispatchGenericMotionEvent(event);        }    }

在DecorView.java的dispatchTouchEvent方法中,如果Activity不為空白,並且沒有被銷毀,則調用Activity的dispatchTouchEvent方法,否則調用父類View的dispatchTouchEvent方法。
@Override        public boolean dispatchTouchEvent(MotionEvent ev) {        // callback就是Activity本身            final Callback cb = getCallback();// 如果Activity不為空白,並且沒有被銷毀,則調用Activity的dispatchTouchEvent// 否則調用父類的dispatchTouchEvent            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)                    : super.dispatchTouchEvent(ev);        }
在Activity的dispatchTouchEvent方法中,調用的是PhoneWindow的superDispatchTouchEvent的方法去分發事件,如果到最後Activity中的所有的view都不去處理這個事件時,就有Activity的OnTouchEvent來處理。
public boolean  dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {// 在dispatch之前做一些操作,其實什麼也沒做            onUserInteraction();        }// 調用PhoneWindow中的superDispatchTouchEvent// PhoneWindow中superDispatchTouchEvent 直接調用了mDecor的superDispatchTouchEvent// mDecore的superDispatchTouchEvent方法中直接調用super.dispatchOnTouchEvent// 也就是開始進入了viewGroup中的dispatchOnTouchEvent方法        if (getWindow().superDispatchTouchEvent(ev)) {            return true;        }//  當Activity中所有的View都不處理Event的時候,就用由Activity的onTouchEvent()來處理// 通知window關閉這個touch事件return onTouchEvent(ev);    }

 public boolean onTouchEvent(MotionEvent event) {        if (mWindow.shouldCloseOnTouch(this, event)) {            finish();            return true;        }                return false;    }
在PhoneWindow的superDispatchTouchEvent中,調用了DecorView的superDispatchTouchEvent方法,進而調用了super.dispatchTouchEvent(event)方法,也就是進入了viewGroup中開始事件的分發。
 @Override    public boolean superDispatchTouchEvent(MotionEvent event) {        return mDecor.superDispatchTouchEvent(event);    }

 public boolean superDispatchTouchEvent(MotionEvent event) {            return super.dispatchTouchEvent(event);        }
ViewGroup的dispatchTouchEvent中,處理如下,如果action是MotionEvent.ACTION_DOWN,則需要重設觸摸的狀態。繼而判斷是否攔截此事件,然後遍曆所有的孩子以便找到一個可以接收此事件的孩子,如果child不存在TouchTarget中,則把事件分發給子view,這個尋找是根據view的地區來尋找的。如果childView沒有消費掉此事件,則 自己處理的事件,如果自己也沒有處理,回溯至父view處理,否則viewGroup把事件一級一級的遞迴傳遞,如果child是一個viewGroup,則重複上述的步驟,如果是view,直接調用dispatchTouchEvent方法。
 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.                // ACTION_DOWN意味著touch事件的改變,所以需要把之前的TouchTargets和TouchState都clear掉,mFirstTouchTarget = null                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) {// 攔截事件,預設返回false,表示不攔截,如果攔截了就不繼續往下面執行了                    intercepted = onInterceptTouchEvent(ev);                    ev.setAction(action); // restore action in case it was changed                } else {                    intercepted = false;                }            } else {                // There are no touch targets and this action is not an initial down                // so this view group continues to intercept touches.                intercepted = true;            }            // 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);                        final boolean customOrder = isChildrenDrawingOrderEnabled();                        for (int i = childrenCount - 1; i >= 0; i--) {                            final int childIndex = customOrder ?                                    getChildDrawingOrder(childrenCount, i) : i;                            final View child = children[childIndex];                            if (!canViewReceivePointerEvents(child)                                    || !isTransformedTouchPointInView(x, y, child, null)) {                                continue;                            }// 判斷child是否在TouchTarget中                            newTouchTarget = getTouchTarget(child);                            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);// child不存在TouchTarget中,則調用dispatchTransformedTouchEvent// 把event分發給子view,這裡並不是做處理,應該就是找到touch地區                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {                                // Child wants to receive touch within its bounds.                                mLastTouchDownTime = ev.getDownTime();                                mLastTouchDownIndex = childIndex;                                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) {// mFirstTouchTarget 為空白,表示childview沒有將此事件消費掉,則自己處理這個event// 如果viewGroup自己也沒有處理,則回溯到父view進行處理                // 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;// viewGroup把事件遞迴傳遞,如果child是一個gourp,則重複上述步驟// 如果是view,則直接調用dispatchTouchEvent方法                        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;    }
在View.java中的 dispatchTouchEvent方法中,如果已經註冊了listener監聽器並且是enable的,並且監聽器的onTouch返回true,則onTouchEvent不會被調用。
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)) {                // 要想執行onTouchEvent方法上述三個條件只要一個不滿足就可以了                //                 return true;            }            if (onTouchEvent(event)) {                return true;            }// 如果所有的View都不處理TouchEvent,最後由Activity來處理        }        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);        }        return false;    }

如果Activity中所有的view都不處理這個事件,則由Activity自己處理
public boolean onTouchEvent(MotionEvent event) {        if (mWindow.shouldCloseOnTouch(this, event)) {            finish();            return true;        }                return false;    }

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.