联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

安卓 Input 机制(4)ViewRootImpl的分发策略 --- InputStage

作者:访客发布时间:2024-01-04分类:程序开发学习浏览:71


导读:前言在第一篇#安卓Input机制(1)整体流程打通-3.3.2章节,讲解了从dispatchInputEvent到deliverInputEvent开始进入Inp...

前言

3-3客户端处理.png

在第一篇# 安卓 Input 机制(1)整体流程打通 - 3.3.2章节 ,讲解了从dispatchInputEvent 到deliverInputEvent开始进入InputStage策略的处理,然后InputStage策略发起向Activity分发数据调用到dispatchPointerEvent方法,参考上图。由于前文目的在于打通整体流程,忽略了细节,因此图中流程并不完整,仅展现了向Activity分发的这一个Stage的处理。本文就讲解一下前文略去的完整的InputStage策略,我们从入口deliverInputEvent方法开始。

deliverInputEvent

private void deliverInputEvent(QueuedInputEvent q) {
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
    }

    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }

    if (stage != null) {
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

可以看到在deliverInputEvent方法中对数据q,使用stage.deliver进行处理。这个InputStage是一个利用了责任链模式的一套对事件q分阶段处理的机制。来看一下他是如何运作的。

1 InputStage策略的运转机制

从代码中可以看到InputStage的赋值有三个判断分支,分别赋值的是mSyntheticInputStage、mFirstPostImeInputStage和mFirstInputStage,我们来看一下这三个值是什么:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
            // Set up the input pipeline.
            CharSequence counterSuffix = attrs.getTitle();
            mSyntheticInputStage = new SyntheticInputStage();
            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                    "aq:native-post-ime:" + counterSuffix);
            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
            InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                    "aq:ime:" + counterSuffix);
            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                    "aq:native-pre-ime:" + counterSuffix);

            mFirstInputStage = nativePreImeStage;
            mFirstPostImeInputStage = earlyPostImeStage;
    ...

首先介绍一下,这些Stage的构造都是直接调用了父类InputStage的构造

abstract class InputStage {
    private final InputStage mNext;

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

很容易可以看出来在此时是构建了一个链表

nativePreImeStage -> viewPreImeStage-> imeStage-> earlyPostImeStage-> nativePostImeStage-> viewPostImeStage-> mSyntheticInputStage

mFirstInputStage = nativePreImeStage 是从nativePreImeStage开始的完整链表, mFirstPostImeInputStage = earlyPostImeStage 是从earlyPostImeStage开始的链表, mSyntheticInputStage = new SyntheticInputStage 就只有一个SyntheticInputStage对象

搞清楚了结构现在看一下他具体如何运作:

1.1 InputStage:

是所有Stage的超类

// abstract class InputStage:

// 起点
public final void deliver(QueuedInputEvent q) {
    // FLAG_FINISHED 后续轮转都
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        // 继续向后传给后续的InputStage,由于q的FLAG_FINISHED一直都在,
        // 后续的InputStage也都会走这个分支,一直传递到最后一个InputStage。
        forward(q); 
    } else if (shouldDropInputEvent(q)) {
        // shouldDropInputEvent 检查一些mView被remove掉了或者窗口非焦点状态等的情况
        // 处理同是给q加上FLAG_FINISHED标签并forward(q),后续的传递进入FLAG_FINISHED分支
        finish(q, false);
    } else {
        // 实际的处理分支
        // 会回调onProcess,各阶段的处理也是通过onProcess实现,并反馈结果
        // apply 执行时会根据onProcess返回状态做相应处理
        apply(q, onProcess(q));
    }
}
// 给q打上FLAG_FINISHED标签并传递给下一个InputStage
protected void finish(QueuedInputEvent q, boolean handled) {
    q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
    if (handled) {
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
    }
    forward(q);
}
// 用于继续向后传递的方法,把q传递给下一个InputStage处理
protected void forward(QueuedInputEvent q) {
    onDeliverToNext(q);
}
protected void onDeliverToNext(QueuedInputEvent q) {
    if (mNext != null) {
        mNext.deliver(q);
    } else {
        finishInputEvent(q);
    }
}
// 针对各状态做对应的分发
protected void apply(QueuedInputEvent q, int result) {
    // 返回状态主要这几个:FORWARD、FINISH_HANDLED、FINISH_NOT_HANDLED
    if (result == FORWARD) {
        // FORWARD状态,继续向下一个阶段流转
        forward(q);
    } else if (result == FINISH_HANDLED) {
        // FINISH_HANDLED结束状态,
        finish(q, true);
    } else if (result == FINISH_NOT_HANDLED) {
        finish(q, false);
    } else {
        throw new IllegalArgumentException("Invalid result: " + result);
    }
}

protected int onProcess(QueuedInputEvent q) {
    return FORWARD;
}

这些是InputStage对事件在Stage链表中流转做控制的几个关键方法

  1. deliver: 流转的起点,根据QueuedInputEvent的标记、状态等做对应的处理

    • FLAG_FINISHED标记分支: 对于由FLAG_FINISHED标记的event,直接调用forward(q)继续向下一个Stage传递,后续Stage的deliver也都会走这个分支。其意义就是该event不做onProcess处理,直接向下传递直到末尾处理finishInputEvent;
    • shouldDropInputEvent分支: 是针对一些需要drop的情况,包括view已经被删除、窗口非焦点状态等;
    • else分支: 正常处理分支,回调onProcess方法,这个方法是实际做事件向View分发的方法。
  2. forward: 表示继续向后传递事件,由onDeliverToNext方法实现:如果有下一个Stage,就发起下一个Stage的deliver,如果没有就调用自己的finishInputEvent,做finishing处理

  3. apply: 接收QueuedInputEvent和onProcess返回的状态

    • FORWARD状态:调用forward直接流转到下一个Stage
    • FINISH_HANDLED状态:进入finish(q, true)
    • FINISH_NOT_HANDLED状态:进入finish(q, false)
    • 其他状态: 抛出异常
  4. finish: 处理需要进入finish状态的QueuedInputEvent,实际就是给其打上FLAG_FINISHED标记,后续该QueuedInputEvent的轮转就一直走finish分支直接传到最后的Stage

InputStage向后传递的控制流程如图:

InputStage1.png

  1. deliver 判断,evnet有 FLAG_FINISHED 标签:直接forward一直向后进入到下一个Stage。后续的Stage 执行deliver判断,event仍有FLAG_FINISHED标签,也就是说有FLAG_FINISHED标签的事件基本上没有什么处理一路向后走到最末尾的Stage去执行它的finishInputEvent方法。
  2. shouldDropInputEvent分支:做一些判断断定当前需要丢弃事件,便给事件打上FLAG_FINISHED标签走和1一样的过程。
  3. 以上都不成立:回调onProcess,这个方法是给具体InputStage实现类做事情的,该方法需要返回一个状态,由不同的Stage处理后给出,然后调用apply根据状态做对应处理。
  4. apply接到FORWARD状态:直接调用forward找下一个Stage做处理。
  5. apply接到FINISH_XXX:调用finish,给事件打上FLAG_FINISHED标签然后走和1一样的的路。
  6. apply接到除4和5外的其他状态,抛异常。

可以看出,各个Stage主要执行操作的地方是onProcess

1.2 AsyncInputStage:

另外,还有一个抽象类 AsyncInputStage 类继承自 InputStage,也可以作为Stage的父类。他添加了一个DEFER状态,从他的类名看可能是异步相关的处理,具体看一下:

他重写了forward和apply两个方法,看来他要对给向下一个Stage的分发的策略中添加一些控制

// AsyncInputStage
protected static final int DEFER = 3;
@Override
protected void forward(QueuedInputEvent q) {
    // 去除标记表示q已经进来过forward了
    q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;

    QueuedInputEvent curr = mQueueHead;
    // 阻塞队列空,不存在阻塞,直接向后面的Stage传递
    if (curr == null) {
        super.forward(q);
        return;
    }

    final int deviceId = q.mEvent.getDeviceId();
    QueuedInputEvent prev = null;
    boolean blocked = false;
    while (curr != null && curr != q) { // 当curr == q跳出循环,表示在队列中找到目标q
        // 因为循环体会判断找到目标就停止循环,如果走到这里就说明在目标q之前发现了该设备的其他事件,需block
        // 从这里可以看出,阻塞队列的处理是从头部向后顺序执行的,出现未处理就会阻塞其后面的元素
        if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
            blocked = true;
        }
        prev = curr;
        curr = curr.mNext;
    }
    
    if (blocked) { // 表示有阻塞的事件,不能继续分发
        if (curr == null) {
            enqueue(q);
        }
        return;
    }

    if (curr != null) {
        curr = curr.mNext;
        dequeue(q, prev);
    }
    // 走到这里,说明当前事件前面没有block可以继续执行,调用父类forward继续向后分发
    super.forward(q);

    // 说明是在队列遍历中找到的目标,需要继续向后遍历后续元素,可能还有被自己阻塞的事件待处理。
    // 因为从前面的代码可以看出,如果是排在本事件后面的元素先执行了forward,
    // 会被本元素阻塞,本循环就是要找这些元素并执行他们。
    while (curr != null) {
        if (deviceId == curr.mEvent.getDeviceId()) {
            // 判断有FLAG_DEFERRED表示还没进来过(因为本方法一开始就是把标记去掉),说明他还没被执行完,这里不能做处理
            if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
                break;
            }
            QueuedInputEvent next = curr.mNext;
            dequeue(curr, prev);
            super.forward(curr);
            curr = next;
        } else {
            prev = curr;
            curr = curr.mNext;
        }
    }
}

@Override
protected void apply(QueuedInputEvent q, int result) {
    // 可以看到对DEFER状态只执行defer(q入链表),我们知道apply方法是向后执行下一个Stage的必经之路
    // 这表示 DEFER 只是把q记录下来让他等待处理,本次不会向后面的Stage分发。
    if (result == DEFER) {
        defer(q);
    } else {
        super.apply(q, result);
    }
}

protected void defer(QueuedInputEvent q) {
    // 添加 FLAG_DEFERRED 标记
    q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
    // enqueue方法里就是一个把q放入一个链表的动作
    enqueue(q);
}
final class NativePreImeInputStage extends AsyncInputStage
        implements InputQueue.FinishedInputEventCallback {
    public NativePreImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

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

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            // finish 最终还是会走向 forward
            finish(q, true);
            return;
        }
        // 事件执行完毕,调用forward继续
        forward(q);
    }
}

结合其实现类看就明白了。onProcess 使用mInputQueue发送事件异步处理,不用等待处理完毕,直接返回DEFER,接下来apply针对DEFER,将其放入等待队列,当异步处理完成会回调onFinishedInputEvent,执行forward,forward会继续处理向下分发。

我们以一个例子来模拟一下处理过程。假如有两个事件A和B先后进入:

  1. onProcess会分别sendInputEvent发起处理他们并返回DEFER使他们进入等待链表。链表:A->B
  2. 此时完成B回调onFinishedInputEvent执行forward(B)
  3. 进入forward第一个while循环先发现A,标记block,然后curr指向B跳出循环,接下来blocked判断命中并返回,没有做处理
  4. 此时完成A回调onFinishedInputEvent执行forward(A)
  5. 进入forward第一个while循环到A直接跳出循环没有标记block,blocked判断未命中继续,没有做处理curr指向A非空,curr后移并把A出队,执行super.forward(A)向后面的Stage分发
  6. 执行完父类forward继续Async的forward,进入最后的while循环,找到了B,和A是同一设备,并且判断B没有FLAG_DEFERRED标记说明已经进来过了,B是被阻塞待处理的,把他出队并执行super.forward(B)向后面的Stage分发,当然如果后面还有符合条件的被阻塞的也会逐个执行,知道遇到一个还没有处理完的元素,则继续阻塞它及其以后的元素。

总结一下:

  1. Stage入口为deliver,会根据状态决定处理行为,对于FINISH类状态和异常状态,直接向下分发直到末尾的Stage,或者干脆直接异常
  2. 非异常状态的事件,Stage会执行自己的onProcess对事件进行处理并返回状态结果,此时要通过apply再对返回的状态结果作处理:FINISH或异常状态同1流程,FORWARD状态是正常状态,继续向下流转到下一个Stage执行deliver
  3. AsyncInputStage类的Stage在流转途中添加了自己的策略,添加了DEFER状态,执行onProcess时对于需要异步处理的任务返回DEFER状态,Stage在流转过程中会做判断,针对DEFER事件放入等待队列,不向下分发;当异步执行完毕回调onFinishedInputEvent时会再次调用forward请求向下流转,其中会有一系列判断假如不阻塞才会继续向下。

到这里,整体运转机制清楚了,我们具体看一下各个Stage

2 各个Stage发挥的作用

2.1 NativePreImeInputStage

final class NativePreImeInputStage extends AsyncInputStage
        implements InputQueue.FinishedInputEventCallback {
    public NativePreImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

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

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

这个stage在上一章节作为异步处理的例子讲过了。他的目的是将事件发送给 native Activity 处理,可以看到仅针对KeyEvent事件。这一传递是在所有处理之前发出的。

2.2 ViewPreImeInputStage

final class ViewPreImeInputStage extends InputStage {
    public ViewPreImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        // 针对 KeyEvent
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        }
        return FORWARD;
    }

    private int processKeyEvent(QueuedInputEvent q) {
        final KeyEvent event = (KeyEvent)q.mEvent;
        // dispatchKeyEventPreIme 返回true返回FINISH_HANDLED表示结束分发
        if (mView.dispatchKeyEventPreIme(event)) {
            return FINISH_HANDLED;
        }
        return FORWARD;
    }
}

表示在IME处理前的分发,他通过调用控件的dispatchKeyEventPreIme方法的返回值,判定是否拦截事件。比如控件重写dispatchKeyEventPreIme方法或onKeyPreIme方法返回true,可以使Stage返回FINISH_HANDLED状态,表示跳过后面的Stage处理,达到拦截的目的。

// View.java
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        return onKeyPreIme(event.getKeyCode(), event);
    }
// ViewGroup.java
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
                == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
            return super.dispatchKeyEventPreIme(event);
        } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
                == PFLAG_HAS_BOUNDS) {
            return mFocused.dispatchKeyEventPreIme(event);
        }
        return false;
    }

该Stage的目的就是在IME执行前给View一个机会处理事件。

2.3 ImeInputStage

final class ImeInputStage extends AsyncInputStage
        implements InputMethodManager.FinishedInputEventCallback {
    public ImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (mLastWasImTarget && !isInLocalFocusMode()) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null) {
                final InputEvent event = q.mEvent;
                if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
                // 事件交给输入法处理
                int result = imm.dispatchInputEvent(event, q, this, mHandler);
                if (result == InputMethodManager.DISPATCH_HANDLED) {
                    return FINISH_HANDLED;
                } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                    // The IME could not handle it, so skip along to the next InputStage
                    return FORWARD;
                } else {
                    return DEFER; // callback will be invoked later
                }
            }
        }
        return FORWARD;
    }

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

将事件分发给输入法做处理。--- imm.dispatchInputEvent

2.4 EarlyPostImeInputStage

final class EarlyPostImeInputStage extends InputStage {
    public EarlyPostImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    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;
    }

    private int processKeyEvent(QueuedInputEvent q) {
        final KeyEvent event = (KeyEvent)q.mEvent;

        if (mAttachInfo.mTooltipHost != null) {
            mAttachInfo.mTooltipHost.handleTooltipKey(event);
        }

        // If the key's purpose is to exit touch mode then we consume it
        // and consider it handled.
        if (checkForLeavingTouchModeAndConsume(event)) {
            return FINISH_HANDLED;
        }

        // Make sure the fallback event policy sees all keys that will be
        // delivered to the view hierarchy.
        // 交给 AudioManager 做处理
        mFallbackEventHandler.preDispatchKeyEvent(event);
        return FORWARD;
    }

    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;

        // Translate the pointer event for compatibility, if needed.
        if (mTranslator != null) {
            mTranslator.translateEventInScreenToAppWindow(event);
        }

        // Enter touch mode on down or scroll, if it is coming from a touch screen device,
        // exit otherwise.
        final int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
            ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
        }

        if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
            mAttachInfo.mTooltipHost.hideTooltip();
        }

        // Offset the scroll position.
        if (mCurScrollY != 0) {
            event.offsetLocation(0, mCurScrollY);
        }

        // Remember the touch position for possible drag-initiation.
        if (event.isTouchEvent()) {
            mLastTouchPoint.x = event.getRawX();
            mLastTouchPoint.y = event.getRawY();
            mLastTouchSource = event.getSource();
        }
        return FORWARD;
    }
}
public void preDispatchKeyEvent(KeyEvent event) {
    getAudioManager().preDispatchKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE);
}

在此阶段将事件交给AudioManager做一些提前处理,以及其他的一些处理(感兴趣可以自行研究)

2.5 NativePostImeInputStage

final class NativePostImeInputStage extends AsyncInputStage
        implements InputQueue.FinishedInputEventCallback {
    public NativePostImeInputStage(InputStage next, String traceCounter) {
        super(next, traceCounter);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (mInputQueue != null) {
            mInputQueue.sendInputEvent(q.mEvent, q, false, this);
            return DEFER;
        }
        return FORWARD;
    }

    @Override
    public void onFinishedInputEvent(Object token, boolean handled) {
        QueuedInputEvent q = (QueuedInputEvent)token;
        if (handled) {
            finish(q, true);
            return;
        }
        forward(q);
    }
}

与NativePreImeInputStage基本一样,只是NativePreImeInputStage仅处理KeyEvent,这里没有做限定

2.6 *** ViewPostImeInputStage

负责向Activity分发事件

final class ViewPostImeInputStage extends InputStage {
    public ViewPostImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    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);
            }
        }
    }

    @Override
    protected void onDeliverToNext(QueuedInputEvent q) {
        if (mUnbufferedInputDispatch
                && q.mEvent instanceof MotionEvent
                && ((MotionEvent)q.mEvent).isTouchEvent()
                && isTerminalInputEvent(q.mEvent)) {
            mUnbufferedInputDispatch = false;
            scheduleConsumeBatchedInput();
        }
        super.onDeliverToNext(q);
    }

    private int processKeyEvent(QueuedInputEvent q) {
        final KeyEvent event = (KeyEvent)q.mEvent;

        Log.e("ViewRootImpl", "processKeyEvent event:" + event.getKeyCode() + ", who:" + Integer.toHexString(mView.hashCode()) + " - [" + mView.toString() + "]");
        // Deliver the key to the view hierarchy.
        // 这里的mView就是DecorView,通过它开启事件分发
        if (mView.dispatchKeyEvent(event)) {
            return FINISH_HANDLED;
        }

        if (shouldDropInputEvent(q)) {
            return FINISH_NOT_HANDLED;
        }

        int groupNavigationDirection = 0;

        if (event.getAction() == KeyEvent.ACTION_DOWN
                && event.getKeyCode() == KeyEvent.KEYCODE_TAB) {
            if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) {
                groupNavigationDirection = View.FOCUS_FORWARD;
            } else if (KeyEvent.metaStateHasModifiers(event.getMetaState(),
                    KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) {
                groupNavigationDirection = View.FOCUS_BACKWARD;
            }
        }

        // If a modifier is held, try to interpret the key as a shortcut.
        if (event.getAction() == KeyEvent.ACTION_DOWN
                && !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
                && event.getRepeatCount() == 0
                && !KeyEvent.isModifierKey(event.getKeyCode())
                && groupNavigationDirection == 0) {
            if (mView.dispatchKeyShortcutEvent(event)) {
                return FINISH_HANDLED;
            }
            if (shouldDropInputEvent(q)) {
                return FINISH_NOT_HANDLED;
            }
        }

        // Apply the fallback event policy.
        if (mFallbackEventHandler.dispatchKeyEvent(event)) {
            return FINISH_HANDLED;
        }
        if (shouldDropInputEvent(q)) {
            return FINISH_NOT_HANDLED;
        }

        // Handle automatic focus changes.
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            if (groupNavigationDirection != 0) {
                if (performKeyboardGroupNavigation(groupNavigationDirection)) {
                    return FINISH_HANDLED;
                }
            } else {
                if (performFocusNavigation(event)) {
                    return FINISH_HANDLED;
                }
            }
        }
        return FORWARD;
    }

    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("ViewRootImpl", "processKeyEvent event:" + event.getAction() + ", who:" + Integer.toHexString(mView.hashCode()) + " - [" + mView.toString() + "]");
        }
        mAttachInfo.mUnbufferedDispatchRequested = false;
        mAttachInfo.mHandlingPointerEvent = true;
        // 这里的mView就是DecorView,通过它开启事件分发
        boolean handled = mView.dispatchPointerEvent(event);
        maybeUpdatePointerIcon(event);
        maybeUpdateTooltip(event);
        mAttachInfo.mHandlingPointerEvent = false;
        if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
            mUnbufferedInputDispatch = true;
            if (mConsumeBatchedInputScheduled) {
                scheduleConsumeBatchedInputImmediately();
            }
        }
        return handled ? FINISH_HANDLED : FORWARD;
    }
}

这里就是负责传递给Activity做处理的地方,可以看到,几乎在最后阶段,像前面讲的AudioManager、IME等的处理都在他之前。

从代码可以看到,按键事件和触摸事件分别通过调用mView.dispatchKeyEvent(event) 和mView.dispatchPointerEvent(event)方法开启,其中的mView就是DecorView。至此进入View体系的事件分发,转向下一章节讲解。

2.7 SyntheticInputStage

final class SyntheticInputStage extends InputStage {
    private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
    private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
    private final SyntheticTouchNavigationHandler mTouchNavigation =
            new SyntheticTouchNavigationHandler();
    private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler();

    public SyntheticInputStage() {
        super(null);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
        if (q.mEvent instanceof MotionEvent) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            final int source = event.getSource();
            if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                mTrackball.process(event);
                return FINISH_HANDLED;
            } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                mJoystick.process(event);
                return FINISH_HANDLED;
            } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
                    == InputDevice.SOURCE_TOUCH_NAVIGATION) {
                mTouchNavigation.process(event);
                return FINISH_HANDLED;
            }
        } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) {
            mKeyboard.process((KeyEvent)q.mEvent);
            return FINISH_HANDLED;
        }

        return FORWARD;
    }

    @Override
    protected void onDeliverToNext(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
            // Cancel related synthetic events if any prior stage has handled the event.
            if (q.mEvent instanceof MotionEvent) {
                final MotionEvent event = (MotionEvent)q.mEvent;
                final int source = event.getSource();
                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    mTrackball.cancel(event);
                } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                    mJoystick.cancel(event);
                } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
                        == InputDevice.SOURCE_TOUCH_NAVIGATION) {
                    mTouchNavigation.cancel(event);
                }
            }
        }
        super.onDeliverToNext(q);
    }
}

放在最后,处理一些其他外部设备如轨迹球、游戏手柄等事件,有需要可以研究。

3 View体系的事件分发

接2.6章节继续

// View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

mView.dispatchPointerEvent(event)这里的mView就是DecorView,dispatchPointerEvent方法在父类View中实现,如上,继续调用了dispatchTouchEvent,dispatchTouchEvent在DecorView中有实现:

// DecorView.java
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 
    final Window.Callback cb = mWindow.getCallback();
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}

这里的cb是Activity,来源如下:

// Activity.java
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    // 把自己注册为mWindow的callback
    mWindow.setCallback(this);
// Window 是 PhoneWindow的父类
public void setCallback(Callback callback) {
    mCallback = callback;
}
public final Callback getCallback() {
    return mCallback;
}

DecorView执行了cb.dispatchTouchEvent(ev),也就是Activity的dispatchTouchEvent

// Activity.java
/**
 * Called to process touch screen events.  You can override this to
 * intercept all touch screen events before they are dispatched to the
 * window.  Be sure to call this implementation for touch screen events
 * that should be handled normally.
 *
 * @param ev The touch screen event.
 *
 * @return boolean Return true if this event was consumed.
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

Activity调用了getWindow().superDispatchTouchEvent(ev),即 PhoneWindow的superDispatchTouchEvent

// PhoneWindow.java
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
// DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}
// ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...

至此,便进入了View系统的消息分发机制,参考文章 安卓 触摸事件分发机制

前面的这个过程可以看到,事件从DecorView分发给Activity,又由Activity重新分发给了DecorView,这是为什么呢?这个从 Activity的dispatchTouchEvent方法的注释中有解释,就是给Activity拦截一切的权利。我们可以重写Activity的dispatchTouchEvent方法做自己的业务,假如不希望View处理事件,就不调用父类的dispatchTouchEvent以达到拦截的目的,如果仍然希望View能被继续分发,super.dispatchTouchEvent即可。

至此,本文结束。


标签:机制策略InputInputStageViewRootImpl


程序开发学习排行
最近发表
网站分类
标签列表