如何实现Android Java层事件传递
白羽 2018-06-05 来源 :网络 阅读 938 评论 0

摘要:本文将带你了解如何实现Android Java层事件传递,1、在WindowInputEventReceiver这个接口中接受从Framework层传递过来的输入事件。 2、使用QueuedInputEvent对所有的事件进行排队,然后根据不同的条件,状态把事件分发下去。 3、在处理事件的时候使用InputStage能够实现对事件的按照顺序处理。 4、在ViewPostImeInputStage对事件进行分发,根据事件类别分发到DecorView中。 希望本文对大家学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方法


   

@

Override
protected 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


   

@Override
public 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频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程