摘要:本文将带你了解如何实现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频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号