Android Java层事件传递

来源:互联网 发布:淘宝落地页是什么 编辑:程序博客网 时间:2024/05/22 00:44

这里写图片描述

android事件传递如图所示,我们下面重点关注的是上图中红色框内的处理流程。

一、Activiy的事件响应接口Callback

首先看Activity的实现,如下,Activity实现了一个特殊的接口:Window.Callback。

public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory2,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks2,        Window.OnWindowDismissedCallback {    private static final String TAG = "Activity";    private static final boolean DEBUG_LIFECYCLE = false;

Window.Callback就是事件回调接口

/** * API from a Window back to its caller.  This allows the client to * intercept key dispatching, panels and menus, etc. */public interface Callback {    /**     * Called to process key events.  At the very least your     * implementation must call     * {@link android.view.Window#superDispatchKeyEvent} to do the     * standard key processing.     *     * @param event The key event.     *     * @return boolean Return true if this event was consumed.     */    public boolean dispatchKeyEvent(KeyEvent event);    /**     * Called to process touch screen events.  At the very least your     * implementation must call     * {@link android.view.Window#superDispatchTouchEvent} to do the     * standard touch screen processing.     *     * @param event The touch screen event.     *     * @return boolean Return true if this event was consumed.     */    public boolean dispatchTouchEvent(MotionEvent event);    /**     * Called to process trackball events.  At the very least your     * implementation must call     * {@link android.view.Window#superDispatchTrackballEvent} to do the     * standard trackball processing.     *     * @param event The trackball event.     *     * @return boolean Return true if this event was consumed.     */    public boolean dispatchTrackballEvent(MotionEvent event);    ...(省略若干代码,下同)

在Activity被创建之后,会调用attach方法。

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) {    attachBaseContext(context);    mFragments.attachHost(null /*parent*/);    mWindow = new PhoneWindow(this);    mWindow.setCallback(this);    mWindow.setOnWindowDismissedCallback(this);    mWindow.getLayoutInflater().setPrivateFactory(this);

Activity实现了事件的回调接口,mWindow通过setCallback(this)持有了Activity的引用,也就是Activity的响应相应是通过Window来回调的。

二、ViewRootImpl的事件处理

一个事件产生,最终会回到WindowInputEventReceiver的onInputEvent方法中进行处理,下面看看这个方法。

ViewRootImpl$WindowInputEventReceiver.java

final class WindowInputEventReceiver extends InputEventReceiver {    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {        super(inputChannel, looper);    }    @Override    public void onInputEvent(InputEvent event) {        enqueueInputEvent(event, this, 0, true);    }    @Override    public void onBatchedInputEventPending() {        scheduleConsumeBatchedInput();    }    @Override    public void dispose() {        unscheduleConsumeBatchedInput();        super.dispose();    }}

可以看到会调用enqueueInputEvent方法。

void enqueueInputEvent(InputEvent event) {    enqueueInputEvent(event, null, 0, false);}void enqueueInputEvent(InputEvent event,        InputEventReceiver receiver, int flags, boolean processImmediately) {    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);    QueuedInputEvent last = mPendingInputEventTail;    if (last == null) {        mPendingInputEventHead = q;        mPendingInputEventTail = q;    } else {        last.mNext = q;        mPendingInputEventTail = q;    }    mPendingInputEventCount += 1;    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,            mPendingInputEventCount);    if (processImmediately) {        doProcessInputEvents();    } else {        scheduleProcessInputEvents();    }}

直接看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;        mPendingInputEventCount -= 1;        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,                mPendingInputEventCount);        deliverInputEvent(q);    }    // We are done processing all input events that we can process right now    // so we can clear the pending flag immediately.    if (mProcessInputEventsScheduled) {        mProcessInputEventsScheduled = false;        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);    }}

上面的代码中最重要的就是deliverInputEvent(q)

private void deliverInputEvent(QueuedInputEvent q) {    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");    try {        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);        }        InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;        if (stage != null) {            stage.deliver(q);        } else {            finishInputEvent(q);        }    } finally {        Trace.traceEnd(Trace.TRACE_TAG_VIEW);    }}

在ViewRootImpl中,有一系列类似于InputStage(输入事件舞台)的概念,每种InputStage可以处理一定的事件类型,比如AsyncInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。当一个InputEvent到来时,ViewRootImpl会寻找合适它的InputStage来处理。

我们可以在ViewRootImpl的setView方法中找到上面出现的三种InputStage:

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;

下面看看ViewPostImeInputStage的onProcess方法

@Overrideprotected int onProcess(QueuedInputEvent q) {    if (q.mEvent instanceof KeyEvent) {        return processKeyEvent(q);    } else {        // If delivering a new non-key event, make sure the window is        // now allowed to start updating.        handleDispatchDoneAnimating();        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);        }    }}

根据不同的事件做了不同的处理,下面来看看processKeyEvent方法

private int processPointerEvent(QueuedInputEvent q) {    final MotionEvent event = (MotionEvent)q.mEvent;    mAttachInfo.mUnbufferedDispatchRequested = false;    boolean handled = mView.dispatchPointerEvent(event);    if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {        mUnbufferedInputDispatch = true;        if (mConsumeBatchedInputScheduled) {            scheduleConsumeBatchedInputImmediately();        }    }    return handled ? FINISH_HANDLED : FORWARD;}

mView就是DecorView,最终调用的就是DecorView的processKeyEvent方法。

 private int processKeyEvent(QueuedInputEvent q) {        final KeyEvent event = (KeyEvent)q.mEvent;        // Deliver the key to the view hierarchy.        if (mView.dispatchKeyEvent(event)) {            return FINISH_HANDLED;        }        if (shouldDropInputEvent(q)) {            return FINISH_NOT_HANDLED;        }        //省略一部分代码}

我们来总结一下在ViewRootImpl中都做了哪些事情:

1、在WindowInputEventReceiver这个接口中接受从Framework层传递过来的输入事件。
2、使用QueuedInputEvent对所有的事件进行排队,然后根据不同的条件,状态把事件分发下去。
3、在处理事件的时候使用InputStage能够实现对事件的按照顺序处理。
4、在ViewPostImeInputStage对事件进行分发,根据事件类别分发到DecorView中。

DecorView对事件的处理

public boolean dispatchKeyEvent(KeyEvent event) {    final int keyCode = event.getKeyCode();    final int action = event.getAction();    final boolean isDown = action == KeyEvent.ACTION_DOWN;    // 重点关注,当window没destroy且其Callback非空的话,交给其Callback处理    if (!mWindow.isDestroyed()) {        final Window.Callback cb = mWindow.getCallback();// Activity、Dialog都是Callback接口的实现        final boolean handled = cb != null && mFeatureId < 0 ?                 cb.dispatchKeyEvent(event)//Activity,Dialog。                : super.dispatchKeyEvent(event);//否则直接派发到ViewGroup#dispatchKeyEvent(View层次结构)        if (handled) {            return true;        }    }    return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)            : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);}

还记得之前在Activity的attach方法中调用过mWindow.setCallback(this)吗?这里就派上用场了,直接回调了Activity的dispatchKeyEvent方法。因为Activity实现了Callback接口

下面就到了Activity里面了

public boolean dispatchKeyEvent(KeyEvent event) {    onUserInteraction();    //重点,调用Window的superDispatchKeyEvent(event)方法    Window win = getWindow();    if (win.superDispatchKeyEvent(event)) {        return true;    }    View decor = mDecor;    if (decor == null) decor = win.getDecorView();    //如果View层次没有处理的话,就交给KeyEvent本身的dispatch方法,Activity的各种回调方法会被触发    return event.dispatch(this, decor != null            ? decor.getKeyDispatcherState() : null, this);}

消息的处理回到了Window中。

下面看看PhoneWindow.java

@Overridepublic boolean superDispatchKeyEvent(KeyEvent event) {    return mDecor.superDispatchKeyEvent(event);}

最终的消息的处理还是到了mDecor里面

DecorView.java

public boolean superDispatchKeyEvent(KeyEvent event) {    // Give priority to closing action modes if applicable.    //如果有设置ActionMode,则优先调用ActionMode    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {        final int action = event.getAction();        // Back cancels action modes first.        if (mPrimaryActionMode != null) {            if (action == KeyEvent.ACTION_UP) {                mPrimaryActionMode.finish();            }            return true;        }    }    //继续下发至View层。    return super.dispatchKeyEvent(event);}

因为DecorView的父类是FrameLayout;所以事件从这里开始就正式进入View层次了。

参考文章:
深入理解android 卷3
http://blog.csdn.net/singwhatiwanna/article/details/50775201
http://www.jianshu.com/p/76b3e1913f51
http://wangkuiwu.github.io/2015/01/02/TouchEvent-Activity/

原创粉丝点击