通过堆栈看事件传输

如何调试事件传输

在自己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 {
        ...
    }
    ...
}

image

这里看到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(...);
  1. 通过sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));获取wms服务
  2. 创建activity实例,创建Application实例,调用 activity.attach()方法最终回调目标Activity的onStart,onResume.在attach中创建了mDecor
  3. 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分发

总结以上代码:

  1. 先让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()开始的。

猜你喜欢

转载自blog.csdn.net/wangwei708846696/article/details/79881682