Android事件傳遞

驅(qū)動(dòng)

-> 觸摸屏幕出發(fā)硬件驅(qū)動(dòng)

-> 產(chǎn)生原生態(tài)的內(nèi)核事件

-> Linux內(nèi)核講事件包裝為通用的Event存到/dev/input/event[x]目錄下

SystemServer

當(dāng)系統(tǒng)啟動(dòng)時(shí),SystemServer進(jìn)程會(huì)啟動(dòng)一些列的系統(tǒng)服務(wù),如AMS,WMS等,包括InputManagerService。

InputManagerService

作用:與硬件通信,接收屏幕輸入事件。
內(nèi)部啟動(dòng)了一個(gè)InputReader讀線程,從到/dev/input/目錄拿到事件
InputReader將事件分發(fā)給InputDispatcher線程,進(jìn)行統(tǒng)一的事件分發(fā)調(diào)度
IMS通過(guò)InputChannel進(jìn)程間通訊(socket)將事件傳遞給應(yīng)用進(jìn)程
InputChannel在ViewRootImpl.setView時(shí),進(jìn)行了注冊(cè)
當(dāng)應(yīng)用進(jìn)程接收到消息,通過(guò)native回調(diào)到InputEventReceiver.dispachInputEvent -> ViewRootImpl
當(dāng)時(shí)間分發(fā)完成后,會(huì)調(diào)用finishInputEvent,告知InputDispatcher將事件移除,完成本次事件分發(fā)

InputEventReceiver


// 由native調(diào)用

private void dispatchInputEvent(int seq, InputEvent event) {

    mSeqMap.put(event.getSequenceNumber(), seq);

    // 這里ViewRootImpl中注冊(cè)了一個(gè)WindowInputEventReceiver

    onInputEvent(event);

}

ViewRootImpl

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
            
       mView = view;
       
       // 屏幕輸入事件
       mInputChannel = new InputChannel();
       ...
       
       requestLayout();
       
       // 添加window時(shí),注冊(cè)mInputChannel
       mWindowSession.addToDisplayAsUser(mWindow, ...., mInputChannel, ...);
       
       // 注冊(cè)輸入輸入事件回調(diào)
       mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());
       ...
       
       view.assignParent(this);
       
       ...
}

// 最終調(diào)用到ViewPostImeInputStage 中的 mView.dispatchTouchEvent(event)
// 這里的mView為DecorView
// 責(zé)任鏈模式,最終調(diào)用到ViewPostImeInputStage 中的 mView.dispatchPointerEvent
// 內(nèi)部調(diào)用了 mView.dispatchTouchEvent(event)
// 這里的mView為DecorView
private void deliverInputEvent(QueuedInputEvent q) {
    InputStage stage;
    ....
    // stage賦值操作
    ....
    if (stage != null) {
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

final class WindowInputEventReceiver extends InputEventReceiver {

    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }

    @Override
    public void onInputEvent(InputEvent event) {
        ....
        // 這個(gè)方法最終調(diào)用到deliverInputEvent
        enqueueInputEvent(event, this, 0, true);
    }
}



// 責(zé)任鏈模式
abstract class InputStage {
    private final InputStage mNext;

    public InputStage(InputStage next) {
        mNext = next;
    }

    public final void deliver(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);
        } else if (shouldDropInputEvent(q)) {
            finish(q, false);
        } else {
            traceEvent(q, Trace.TRACE_TAG_VIEW);
            final int result;
            try {
                result = onProcess(q);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            apply(q, result);
        }
    }
}

DecorView


// 調(diào)用到activity的dispatchTouchEvent

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

  final Window.Callback cb = mWindow.getCallback();

  // 這里的mWindow為PhoneWindow,cb為Activity

  return cb != null && !mWindow.isDestroyed() && mFeatureId < 0

            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);

}

// 調(diào)用父類(lèi)即使ViewGroup的dispatchTouchEvent往下分發(fā)事件

public boolean superDispatchTouchEvent(MotionEvent event) {

    return super.dispatchTouchEvent(event);

}

Activity


public boolean dispatchTouchEvent(MotionEvent ev) {

  ...

  // 調(diào)用PhoneWindow的superDispatchTouchEvent

  if (getWindow().superDispatchTouchEvent(ev)) {

      return true;

  }

  return onTouchEvent(ev);

}

PhoneWindow


@Override

public boolean superDispatchTouchEvent(MotionEvent event) {

  // 調(diào)用DecorView

  return mDecor.superDispatchTouchEvent(event);

}

ViewGroup


// 第一個(gè)觸摸目標(biāo),用于判斷是否有childView消費(fèi)事件,鏈表結(jié)構(gòu)

private TouchTarget mFirstTouchTarget;

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

    final int action = ev.getAction();
    final int actionMasked = action & MotionEvent.ACTION_MASK;

        // 是否需要攔截
    final boolean intercepted;
    // ACTION_DOWN 或者 mFirstTouchTarget不為空,判斷一下是否需要攔截
    if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
       // 判斷是否允許攔截,childView通過(guò)調(diào)用ParentView.requestDisallowInterceptTouchEvent設(shè)置      
       final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
       // 允許攔截
       if (!disallowIntercept) {
            // 調(diào)用自身的onInterceptTouchEvent判斷是否需要攔截
          intercepted = onInterceptTouchEvent(ev);
          ...
       } else {
            // 不允許攔截
          intercepted = false;
       }
     } else {
        // 如果mFirstTouchTarget為null且不是ACTION_DOWN,
        intercepted = true;
     }
        
     final boolean canceled = ...;
     TouchTarget newTouchTarget = null;
     boolean alreadyDispatchedToNewTouchTarget = false;
     // 不攔截
     if (!canceled && !intercepted) {
            ...
        // Down事件會(huì)判斷一下是否有子View產(chǎn)生消費(fèi),初始化mFirstTouchTarget
            if (actionMasked == MotionEvent.ACTION_DOWN
           || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
           || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
           ...
           for (int i = childrenCount - 1; i >= 0; i--) {
                 final View child = ...
                 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                  // 添加mFirstTouchTarget鏈表中
                    newTouchTarget = addTouchTarget(child, idBitsToAssign)
                  alreadyDispatchedToNewTouchTarget = true;
                  break;
               }
           }
        }
     }
     
     // 沒(méi)有相應(yīng)的觸摸目標(biāo)
     if (mFirstTouchTarget == null) {
        // 當(dāng)作普通的view進(jìn)行分發(fā)
        handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
     } else {
            // 分發(fā)到觸摸目標(biāo)(整個(gè)鏈表)
            TouchTarget target = mFirstTouchTarget;
        while (target != null) {
                    final TouchTarget next = target.next;
              // 如果已經(jīng)分發(fā)過(guò),則無(wú)需再次分發(fā),排除DOWN事件已經(jīng)分發(fā)過(guò)的事件
              if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                 handled = true;
              } else {
                        final boolean cancelChild = intercepted || ...;
                // 向整個(gè)觸摸目標(biāo)鏈表分發(fā)事件,如果ViewGroup攔截了事件,則分發(fā)取消事件
                if (dispatchTransformedTouchEvent(ev, cancelChild,
                        target.child, target.pointerIdBits)) {
                   handled = true;
                }
                // 如果ViewGroup攔截了事件,當(dāng)遍歷完觸摸目標(biāo)后,mFirstTouchTarget為null
                if (cancelChild) {
                     ...
                   mFirstTouchTarget = next;
                   continue;
                }
              }
              ...
              target = next;
        }
     }
         return handled;

}

// childView事件分發(fā)

// 將運(yùn)動(dòng)事件轉(zhuǎn)換為特定子視圖的坐標(biāo)空間,過(guò)濾掉不相關(guān)的指針 ID,并在必要時(shí)覆蓋其操作。 如果 child 為 null,則 MotionEvent 將改為發(fā)送到此 ViewGroup。

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,

            View child, int desiredPointerIdBits) {

    final boolean handled;

    // 分發(fā)取消的事件

    final int oldAction = event.getAction();

    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {

      event.setAction(MotionEvent.ACTION_CANCEL);

      if (child == null) {

          handled = super.dispatchTouchEvent(event);

      } else {

          handled = child.dispatchTouchEvent(event);

      }

      event.setAction(oldAction);

      return handled;

    }

    // 過(guò)濾不相關(guān)的指針

    // 如果由于某種原因我們最終處于不一致的狀態(tài),看起來(lái)我們可能會(huì)產(chǎn)生一個(gè)沒(méi)有指針的運(yùn)動(dòng)事件,然后刪除該事件。

    if (newPointerIdBits == 0) {

      return false;

    }

    ...

    // 一些變換操作

    final MotionEvent transformedEvent = ...

    if (child == null) {

      // 沒(méi)有子View消費(fèi)事件,則將事件傳遞給父類(lèi),其中會(huì)調(diào)用到onTouchEvent

      handled = super.dispatchTouchEvent(transformedEvent);

    } else {

      final float offsetX = mScrollX - child.mLeft;

      final float offsetY = mScrollY - child.mTop;

      transformedEvent.offsetLocation(offsetX, offsetY);

      handled = child.dispatchTouchEvent(transformedEvent);

    }

    return handled;

}

// 添加mFirstTouchTarget

private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {

    final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);

    target.next = mFirstTouchTarget;

    mFirstTouchTarget = target;

    return target;

}

View


// View響應(yīng)事件,并傳遞到onTouchEvent方法中

public boolean dispatchTouchEvent(MotionEvent event) {

boolean result = false;

  ...

  final int actionMasked = event.getActionMasked();

  if (actionMasked == MotionEvent.ACTION_DOWN) {

      stopNestedScroll();

  }

  ... 

  ListenerInfo li = mListenerInfo;

  // View的OnTouchListener監(jiān)聽(tīng),先于OnClickListener,如果返回true,則OnClickListener不會(huì)響應(yīng)

  if (li != null && li.mOnTouchListener != null

          && (mViewFlags & ENABLED_MASK) == ENABLED

          && li.mOnTouchListener.onTouch(this, event)) {

      result = true;

  }

  // View的onTouchEvent處理,及Click監(jiān)聽(tīng)

  if (!result && onTouchEvent(event)) {

      result = true;

  }

  return result;

}

Click


// View的onTouchEvent處理,及Click監(jiān)聽(tīng)

public boolean onTouchEvent(MotionEvent event) {

final float x = event.getX();

  final float y = event.getY();

  final int viewFlags = mViewFlags;

  final int action = event.getAction();

  final boolean clickable = ...;



  // Disabled狀態(tài)

if ((viewFlags & ENABLED_MASK) == DISABLED) {

      ...

      return clickable;

  }

  if (clickable){

    switch (action) {

        case MotionEvent.ACTION_UP:

        ...

          // 點(diǎn)擊處理及回調(diào)

        performClickInternal();

          break;

        ...

      }

      return true;

  }

return false;



}

一個(gè)事件完整流程

-> 驅(qū)動(dòng)
-> IMS
-> InputEventReceiver
-> ViewRootImpl
-> DecorView(dispatchInputEvent)
-> Activity(dispatchInputEvent)
-> PhoneWindow(superDispatchTouchEvent)
-> DecorView(父類(lèi)ViewGroup的dispatchInputEvent)
-> 分發(fā)給子View
-> 回到ViewRootImpl,結(jié)束事件分發(fā)

文字描述:
1.驅(qū)動(dòng)響應(yīng)事件并包裝為Event
2.通過(guò)InputManagerService將Event傳遞到應(yīng)用進(jìn)程
3.通過(guò)native回調(diào)到InputEventReceiver
4.ViewRootImpl注冊(cè)了InputChannel及InputEventReceiver,所以可以接收到事件,
收到事件后,傳遞給DecorView.dispatchInputEvent,這里使用到了責(zé)任鏈模式
5.DecorView調(diào)用Activity.dispatchInputEvent
6.Activity調(diào)用PhoneWindow.superDispatchTouchEvent
7.PhoneWindow調(diào)用DecorView.superDispatchTouchEvent
8.DecorView調(diào)用父類(lèi)super.dispatchInputEvent
9.當(dāng)沒(méi)有子View消費(fèi),會(huì)調(diào)用Activity.onTouchEvent()
10.不管有沒(méi)有子View消費(fèi),都會(huì)回到ViewRootImp,然后將事件移除,結(jié)束這次事件分發(fā)

示例

RecyclerView的手勢(shì)Down、Move、Up的分發(fā),item的點(diǎn)擊事件分發(fā)邏輯

Down

1.RecyclerView收到Down事件,由于子View沒(méi)有請(qǐng)求攔截事件(disallowIntercept為false),所以會(huì)判斷是否由自身攔截事件(onInterceptTouchEvent)

2.由于Down事件沒(méi)有觸發(fā)RecyclerView的滾動(dòng),所以沒(méi)有攔截(onInterceptTouchEvent返回false),事件會(huì)繼續(xù)往下分發(fā)到子View(dispatchTouchEvent)

3.子View的dispatchTouchEvent會(huì)將事件流轉(zhuǎn)到onTouchEvent,由于子View設(shè)置了點(diǎn)擊事件,所以onTouchEvent返回true表示進(jìn)行消費(fèi)

4.RecyclerView的Down事件分發(fā),有子View產(chǎn)生消費(fèi),所以mFirstTouchTarget不為null

Move

1.RecyclerView收到Move事件,由于mFirstTouchTarget不為null,所以會(huì)繼續(xù)判斷是否由自身攔截事件(onInterceptTouchEvent)

2.如果onInterceptTouchEvent判斷產(chǎn)生了滾動(dòng),則返回true表示要進(jìn)行攔截,會(huì)向mFirstTouchTarget分發(fā)一個(gè)Cancel事件后,將mFirstTouchTarget置為null

3.由于mFirstTouchTarget為null,所以后續(xù)的Move事件都會(huì)被RecyclerView攔截掉,不會(huì)再往下分發(fā),會(huì)將事件分發(fā)給自身的onTouchEvent

Up

1.如果在Move中,RecyclerView產(chǎn)生滾動(dòng)攔截了事件,則后續(xù)Move、Up都不會(huì)傳遞到子View

2.RecyclerView收到Up事件,并且未產(chǎn)生滾動(dòng)事件,會(huì)將事件繼續(xù)分發(fā)到子View(同Down),

3.子View收到Up事件后,onTouchEvent判斷產(chǎn)生了點(diǎn)擊事件后,回調(diào)OnClick

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容