如何调试事件传输
在自己Activity中打印下堆栈
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Thread.dumpStack();
return super.dispatchTouchEvent(ev);
}
W/System.err: java.lang.Throwable: stack dump
at java.lang.Thread.dumpStack(Thread.java:496)
at com.demo.MainActivity.dispatchTouchEvent(MainActivity.java:65)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1901)
at android.view.View.dispatchPointerEvent(View.java:7426)
at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3220)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3165)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4292)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4271)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4363)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:179)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:125)
at android.os.Looper.loop(Looper.java:124)
at android.app.ActivityThread.main(ActivityThread.java:5041)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
ViewRootImpl.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;
if (processImmediately) {
doProcessInputEvents();//走这里
} else {
scheduleProcessInputEvents();
}
}
可见搞了一个链表,将InputEvent这个事件添加到mPendingInputEventTail这个链表中
void doProcessInputEvents() {
//进行遍历这个链表进行事件的处理
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
...
deliverInputEvent(q);//走这里
}
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
这里就是对链表缓存中的事件进行遍历处理
/*事件分发*/
private void deliverInputEvent(QueuedInputEvent q) {
...
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;//这个有一个很神奇
...
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
InputStage是一个抽象父类,有很多子类分别对应着处理不同的事件,比如有键盘事件,虚拟按键,在这里调用的是stage.deliver(q);
谷歌的设计是InputStage的子类用责任链模式,相当于每一个子类都是一个拦截器拦截这个事件,然后看是不是自己需要处理,如果需要处理自己进行处理,如果不需要自己处理则交给下一个进行处理。
至于ViewRootImpl.setView()什么时候调用等会说
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
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);
...
}
ViewPostImeInputStage extends InputStage
这个是InputStage.deliver()
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));//可见是一个个循环调用onProcess(q)
}
}
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) {
if (result == FORWARD) {
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
上面代码可以看出核心处理就是在子类的onProcess()方法中
final class ViewPreImeInputStage extends InputStage {
public ViewPreImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);//进行处理KeyEvent
}
return FORWARD;
}
//交给了mView处理
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mView.dispatchKeyEventPreIme(event)) {
return FINISH_HANDLED;
}
return FORWARD;
}
}
这个mView的由来
WindowManagerGlobal.addView():
root = new ViewRootImpl(view.getContext(), display);
root.setView(view, wparams, panelParentView);//来源这里:
mView = view;
最终到达传递进来的View中的dispatchKeyEventPreIme。那么谁会传递给WindowManagerGlobal.addView()
WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
...
}
接下来会看一个问题就是谁调用了
WindowManagerGlobal.addView()
传递进去的View到底是谁?AT.handleLaunchActivity
这个是在app进程创建之后就要调用的方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
handleConfigurationChanged(null, null);
//获取WMS代理对象
WindowManagerGlobal.initialize();//sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
Activity a = performLaunchActivity(r, customIntent);//创建activity实例,创建Application实例,调用 activity.attach()方法
if (a != null) {
//最终回调目标Activity的onStart,onResume.
handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed);
...
} else {
...
}
...
}
这里看到performLaunchActivity()方法做了创建activity,调用attach()方法
public class Activity extends ContextThemeWrapper implements ... {
private Window mWindow;
final void attach(...) {
...
mWindow = new PhoneWindow(this); //创建PhoneWindow,PhoneWindow对象中创建了mDecor对象
...
//设置并获取WindowManagerImpl对象
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
...
mWindowManager = mWindow.getWindowManager();
...
}
}
继续上面的操作执行handleResumeActivity
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
//执行到onResume方法()
final Activity a = r.activity;
r.activity.makeVisible();//执行activity中的makeVisible
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();//得到WindowManagerImpl对象此对象内部拥有WindowManagerGlobal
wm.addView(mDecor, getWindow().getAttributes());
}
mDecor.setVisibility(View.VISIBLE);
}
这里看到调用wm的addView(),那么mDecor是谁,在哪里创建的。我们上面说PhoneWindow中构造中创建了mDecor我们看下
public PhoneWindow(Context context, Window preservedWindow, ActivityConfigCallback activityConfigCallback) {
...
if (preservedWindow != null) {
mDecor = (DecorView) preservedWindow.getDecorView();
}
...
}
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();//最终调用generateDecor() new了一个DecorView
}
return mDecor;
}
其中featureId=-1
protected DecorView generateDecor(int featureId) {
Context context;
//true
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
public final WindowManager.LayoutParams getAttributes() {
return mWindowAttributes;
}
小结第二段
所以就是说,我们在Activity创建的过程中得到WMS服务,然后调用了Activity.attach()方法,其中创建了mDecor,我们可以按照上面那个图理解
1. WindowManagerGlobal.initialize();
2. Activity a = performLaunchActivity(r, customIntent);
3. handleResumeActivity(...);
- 通过
sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
获取wms服务 - 创建activity实例,创建Application实例,调用 activity.attach()方法最终回调目标Activity的onStart,onResume.在attach中创建了mDecor
- handleResumeActivity()中执行了wm.addView()
所以开始的时候调用的是
mDecor.dispatchTouchEvent()
DecorView.dispatchTouchEvent()
void setWindow(PhoneWindow phoneWindow) {
mWindow = phoneWindow;
...
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
我们知道在new PhoneWindow的时候有如下代码:
Activity.attach()
final void attach(...) {
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
}
所以mDecor是将事件传递给了Activity.dispatchTouchEvent()
Activity.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();//如果按下事件则调用这个方法
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
所以可以看出来蛇形调用事件是先一层层调用dispatchTouchEvent后再次调用onTouchEvent
其中getWindow().superDispatchTouchEvent(ev)
这个方法调用:
getWindow().superDispatchTouchEvent(ev):
mDecor.superDispatchTouchEvent(event):
super.dispatchTouchEvent(event):
ViewGroup.dispatchTouchEvent()
也就是说看 ViewGroup.dispatchTouchEvent()这个方法有没有拦截这个事件,其中为mDecor的ViewGroup,之后交给onTouchEvent()进行处理
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {//看超没超过mDecor的区域,如果超过那就把当前Activity finish并且拦截此事件
finish();
return true;
}
return false;
}
所以最后的过程是交给了mDecor的ViewGroup
这里我们就看看mDecor到底是怎么回事
//可以看出我们控件树的根布局是一个FrameLayout
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
...
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
...
//如果windows没有被销毁,拿到actvity的引用交给activity处理
if (!mWindow.isDestroyed()) {
final Window.Callback cb = mWindow.getCallback();
//如果activity的dispatchKeyEvent已经处理返回true,则此方法总体返回true
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) : super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
//如果activity不拦截最终使用windowphone的处理方法
return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
: mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
}
我们上面也提到了Activity中dispatchKeyEvent的处理方法主要逻辑是:
1. 处理菜单按键
2. 交给PhoneWindow进行superDispatchKeyEvent(event)处理–>然后交给DecorView的superDispatchKeyEvent()进行处理
3. 如果PhoneWindow处理返回false则执行event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this);
主流程事件交给DecorView的superDispatchKeyEvent()处理之后传递给了ViewGroup的dispatchKeyEvent()
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
}
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {//调用View的dispatchKeyEvent()
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) {
//mFocused!=null说明view在ViewGroup
if (mFocused.dispatchKeyEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
总结:ViewGroup是重写了View的dispatchKeyEvent,如果有子view时,分发按键消息到子view中去。没有,直接由父view分发
总结以上代码:
- 先让actionbar优先处理keyEvent,然后通过window处理,处理不了,到window上的DecorView处理。(Activity.dispatchKeyEvent())
主要过程如下:
1、调用onUserInteraction(),可重载该函数在消息派发前做一些处理
2、回调Activity包含的Window对象的superDispatchKeyEvent,该函数继而调用mDecor.superDispatchKveyEent,该函数继而又调用super.dispatchKeyEvent,DecorView的父类是FrameLayout,而FrameLayout未重载dispatchKeyEvent,因此最终调用ViewGroup的dispatchKeyEvent
3、如果DecorView未消耗消息,则调用event的dispatch()函数,这里的第一个参数receiver是Activity对象
写到这,有一个疑问?就是一个消息怎么从window派发到viewRoot中去呢?或者说ViewRoot中的按键消息是从哪来的?
ViewRoot中有一个内部类: W,W是一个Binder子类(static class W extends IWindow.Stub ),用于接收global window manager的各种消息, 如按键消息, 触摸消息等。 ViewRoot有一个W类型的成员mWindow,ViewRoot在构造中创建一个W的instance并赋值给mWindow(mWindow = new W(this);)。 ViewRoot是Handler的子类, W会通过Looper把消息传递给ViewRoot。
W/System.err: java.lang.Throwable: stack dump
at java.lang.Thread.dumpStack(Thread.java:496)
at com.demo.MainActivity.dispatchTouchEvent(MainActivity.java:65)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1901)//上面就进入应用层的事件分发了
...
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3165)//进入InputStage的一系列拦截器
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4292)//处理队列中所有事件
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4271)//添加事件到代队列
再次抽象阐述就是:WMS将事件传递给了ViewRootImpl然后逐个处理队列中的事件派发给拦截器(InputStage子类形成模式类似于责任链模式)最后派发给顶层View最后判断有子View发给子View没有子View自己处理。事件的U型传递也是从Activity.dispatchKeyEvent()开始的。