95992828九五至尊2

窗口与用户输入系统,源码分析

一月 25th, 2019  |  882828九五至尊手机版

《浮窗开发之窗口层级》那片文章中,开篇提议了多少个问题:

大概

继承上一篇小说(View·Input伊芙(Eve)nt事件投递源码分析(一))得出的下结论,本文接着对
View、ViewGroup 的轩然大波派发、拦截举办源码分析。

ViewRootImpl#setView 里的 View 是什么?

上一篇小说得到 View 的屏幕触摸事件的拍卖由 ViewPostImeInputStage
类举行拍卖。

// ViewRootImpl.java
private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            //  ……
            boolean handled = mView.dispatchPointerEvent(event);
            //  ……
            return handled ? FINISH_HANDLED : FORWARD;
        }

【 Tips : 阅读源码的时候,时刻必要带着问题去找答案。】

分析上述代码我们需要先确定 mView 指代的含义,再我们继续分析之前。
先有如下估算:
1、 mView 是赢得宗旨的 View;
2、mView 是顶层的 DecorView;

我们通过查找 mView 的实例,很容易找到 setView 方法。这个方法将 mView 与 ViewRootImpl 互相绑定,mView 的身份已经呼之欲出了。只是在并未丰裕的凭据(代码)表达从前,我们对结果仍维持思疑。

// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...
                requestLayout();
                if (...) {
                    mInputChannel = new InputChannel();
                }
                ...
                try {
                    ...
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    ...
                } finally {
                   ...
                }
            }
        }
}

因为 setView
是实例方法,所以可以将注意点先转移到 ViewRootImpl 对象是如何被创建的。

俺们发现 ViewRootImpl 的构造器权限是 public 的,所以不设有单例调用。

一番查找在 WindowManagerGlobal 的 addView
中找到了如下代码。WindowManagerGlobal
提供与系统窗口管理器的低级别通讯,用于与其余特定上下文毫无干系的操作。

// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // ……
        ViewRootImpl root;
        // ……
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);


        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            ...
            }
            throw e;
        }
}

原来 setView 在 WindowManagerGlobal 中被调用,而传入的 View 就是 DecorView。

前多个问题在前两篇小说中已经分析,在那篇小说中我们以第八个问题为切入点,不难解析一下窗口与用户输入的涉嫌。

ViewRootImpl 与 WindowManagerService 通信分析

WindowManagerImpl 作为
WindowManagerGlobal的代理持有了WindowManagerGlobal对象。

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, mDisplay, mParentWindow);
    }
}

继承跟踪 WindowManagerImpl 对象,发现其已经注册到 WindowManagerService(Service)中。

// SystemServiceRegistry.java
registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
    @Override
    public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx.getDisplay());
}});

在得知WindowManagerImpl与WindowManagerService有关联的情况下,第一时间去WindowManagerService中查找WindowManager对象。但搜索一番自此并不曾察觉WindowManager的实例,可是他们之前一定是有关联的。

既然没有展现的那么直接,那自然是透过中介的花样转载了。而WindowManagerService又有所对
Window 的末尾完成,于是乎大家再一次回到 ViewRootImpl 中找到
setView中的代码片段。

// ViewRootImpl.java
final IWindowSession mWindowSession;

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
}

mWindowSession 顾名思义应是两者之间的会话机制。

public interface IWindowSession extends android.os.IInterface {

   public static abstract class Stub extends android.os.Binder implements android.view.IWindowSession {
        ...
  }
}

而Session是IWindowSession.Stub的兑现类,Session中也有着了WindowManagerService(Service)的实例。

final class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    final WindowManagerService mService;

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}

代码又回去 WindowManagerService(Service)中,最后通过 window 的openInputChannel方法打开了与 ViewRootImpl 通信的渠道。最后是因而InputManagerService(Service) 打开 InputChannel ,提供了广播发布的环境。

// WindowServiceManagerImpl.java
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {


            WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            ...
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
            ...
}

// WindowState

    void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = makeInputChannelName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            // If the window died visible, we setup a dummy input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create dummy event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }

    void disposeInputChannel() {
        if (mDeadWindowEventReceiver != null) {
            mDeadWindowEventReceiver.dispose();
            mDeadWindowEventReceiver = null;
        }

        // unregister server channel first otherwise it complains about broken channel
        if (mInputChannel != null) {
            mService.mInputManager.unregisterInputChannel(mInputChannel);
            mInputChannel.dispose();
            mInputChannel = null;
        }
        if (mClientChannel != null) {
            mClientChannel.dispose();
            mClientChannel = null;
        }
        mInputWindowHandle.inputChannel = null;
    }

Touch事件是什么样分发到Activity上来的?

882828九五至尊手机版 1

Act事件传递流程?

健康的思绪是直接去追寻Activity
的dispatchTouch伊夫(Eve)nt方法,我们看看Activity的dispatchTouch伊夫nt()方法的调用栈,在点子中进入Thread.dumpStack()来查阅调用栈。

 @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    Thread.dumpStack();
    return super.dispatchTouchEvent(ev);
}

输出:

05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk       W/System.err: java.lang.Throwable: stack dump
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at java.lang.Thread.dumpStack(Thread.java:496)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at com.demo.liuguangli.suspendbox.MainActivity.dispatchTouchEvent(MainActivity.java:65)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1901)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.View.dispatchPointerEvent(View.java:7426)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3220)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3165)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4292)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4271)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4363)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:179)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:125)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.os.Looper.loop(Looper.java:124)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5041)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at java.lang.reflect.Method.invokeNative(Native Method)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at java.lang.reflect.Method.invoke(Method.java:511)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
05-09 09:35:39.775 8911-8911/com.yinhan.hunter.dksdk W/System.err:     at dalvik.system.NativeStart.main(Native Method)

一条粗略的端倪:
ViewRootImpl-deliverInput伊芙(Eve)nt
->View.dispatchPointer伊芙nt->PhoneWindow$DecorView.dispatchTouch伊夫nt->MainActivity.dispatchTouch伊夫nt,读者可以根据这些线索去跟踪源码。大家那里先不深切其中细节,先来看看DecorView
到 Activity.dispatchTouch伊夫(Eve)nt 是哪些调用的?

《浮窗开发之窗口层级》一文中,大家有讲到Activity、PhoneWindow、DecorView的关系,我们先来回看一下:

882828九五至尊手机版 2

Activity和window的关系

882828九五至尊手机版 3

window和view的关系

再来看看DecorView的 dispatchTouch伊芙(Eve)nt方法:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Callback cb = getCallback();
        return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                : super.dispatchTouchEvent(ev);
    }

DecorView 是view的子类重写了dispatchTouch伊芙nt方法,在那些措施中调用
Callback,这么些Callback是Window的一个静态内部接口类,Activity已毕了这些接口,Activity的dispatchTouch伊夫(Eve)nt()
方法正是从Callback继承而来。

882828九五至尊手机版 4

Act事件传递流程

DecorView 的风云派发

    // View.java
    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }
    // DecorView.java
    @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);
    }

当有 cb 对象时即可传送给 cb 对象去处理,否则交给 view 去处理。而
Activity 又已毕了 cb 对象的接口。

  • 由此首次的dispatchTouch伊夫nt 事件交给 Activity 去处理。
  • 随即在 Activity 处理时会再一次将事件抛给 DecorView 的
    superDispatchTouch伊夫nt 方法。而 superDispatchTouch伊夫nt 的法门就是
    ViewGroup 的 dispatchTouch伊芙(Eve)nt 方法。
  • 经历了上述两步之后,ViewGroup 的风云分发也就真的含义上的开首了。

// Activity.java
  public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

Touch事件是怎么样分发到浮窗的根视图的?

思路同上:dump出根视图的dispatchTouch伊芙nt()方法调用栈:

05-09 11:58:50.623 11447-11447/com.float.mall W/System.err: java.lang.Throwable: stack dump
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at java.lang.Thread.dumpStack(Thread.java:496)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at com.jym.floatwinplugin.view.widget.FloatBallView.dispatchTouchEvent(FloatBallView.java:250)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.View.dispatchPointerEvent(View.java:7426)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3220)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3165)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4292)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4271)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4363)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:179)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:125)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.os.Looper.loop(Looper.java:124)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5041)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at    java.lang.reflect.Method.invokeNative(Native Method)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at   java.lang.reflect.Method.invoke(Method.java:511)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
05-09 11:58:50.623 11447-11447/com.float.mall W/System.err:     at dalvik.system.NativeStart.main(Native Method)

对待一下和Activity的关系,是否意识有些似曾相识。在《浮窗开发之窗口层级》一文中大家讲过Activity的突显和浮窗的展现本质上是将一个View和呼应的LayoutParams
充裕到WindowManager瑟维斯(Service)中管理。所以Activity的dispatchTouch伊芙nt方法其实是View传递过来的。

咱俩得以揣测粗略线索是:touch事件-》硬件装置-》某个服务-》
ViewRootImpl --》View。

882828九五至尊手机版 5

输入事件传递-某个服务

结束语

到此,事件从接触到分发的首先步已经分析透彻了,下一章将讲述具体的分发进程。那也是基本上博客反复写,且覆盖率分外高的一篇作品。倒不是就是要去造轮子,只是想经过自己对源码的分析加深对散发进度的精通。


[[Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()]:http://blog.csdn.net/xyz\_lmn/article/details/12517911
[Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()]:http://blog.csdn.net/xyz\_lmn/article/details/12517911

ViewRootImpl是个啥?

我们先来看望ViewRootImpl和View到底有吗关系?首先,看看WindowManager的addView方法,WindowManager是个接口,大家看其促成类WindowMangerImpl的源码:

…..
private final WindowManagerGlobal mGlobal =     WindowManagerGlobal.getInstance();
…..

@Override
public void addView(View view, ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

在来看望WindowManagerGlobal的源码:

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { 
    ViewRootImpl root; 
    View panelParentView = null;
    ...这里省略了一堆代码
    root =new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams); mViews.add(view);
    mRoots.add(root); mParams.add(wparams);
  // do this last because it fires off messages to start doing things
try{
    root.setView(view, wparams, panelParentView);
 }catch(RuntimeException e) {
    // BadTokenException or InvalidDisplayException, clean up.
synchronized(mLock) {
    final int index = findViewLocked(view,false);
    if(index >=0) {
       removeViewLocked(index,true);
    }
   }
   throw  e;
  }
}

在那里开创了ViewRootImpl对象,并且把传单下来的view通过setView方法设置到其中的变量,来探视setView的源码:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
     synchronized (this) {
         if (mView == null) {
             mView = view;
            ...省略一堆代码
         }
     }


通过取得关系图:

882828九五至尊手机版 6

ViewRootImpl关系.png

由此WindowManagerImpl.addView,最后把View添加赋值到了ViewRootImpl的变量mView。ViewRootImpl是View(窗口)和WindowManagerService商事的关键
从MVC的角度来看的话:可以认为View是V,ViewRootImpl是Controller,WindowManagerService(Service)是Model。View的绘图、刷新都亟待经过ViewRootImpl与WindowManager瑟维斯(Service)交互,别的View的输入事件(键盘、触摸)也是由ViewRootImpl传递给View的,那么ViewRootImpl是哪些监听到用户输入事件的吧?

用户输入与窗口

回首下上文点到的WindowInput伊芙ntReceiver,那是ViewRootImpl的一个中间类,大家dump出来的dispatchTouch伊芙(Eve)nt最初的地点就是源于那一个类,再往下就是MessageQueue、Looper的消息。由此可以推论WindowInput伊芙(Eve)ntReceiver是ViewRootImpl和尾部某个服务进行IPC交互的要害,这些服务是怎么服务啊?

这一部分事关到Anddroid系统的多少个关键的模块:图形窗口和用户输入,分别对应的劳务是WindowManager瑟维斯(Service)(Service)和InputManagerService(Service)。WindowManager瑟维斯(Service)(Service)负责图形窗口(View)的绘图、刷新等东西、InputManagerService管理用户输入事件处理。

1、InputManagerService(Service) 管理者五个角色InputReader和InputDispatcher 。
2、InputReader负责从硬件(伊夫(Eve)ntHub)读取输入信号,转化成为事件,传递给InputDispatcher。
3、InputDispatcher将Input里德(Reade)r传递过来的轩然大波分发到相应的意况,例如将touch事件分发到ViewRootImpl。

那就是说InputManagerService(InputDispatcher)是怎样将touch事件传递到ViewRootImpl(WindowInput伊芙ntReceiver)的吧?

用户输入事件处理模型是“生产者-消费者“模型,生产者发生在系统经过中,消费者爆发在用户进度中。传递进程由IPC交互,那里的简报是行使的Socket通信,消费者须要向生产者”注册“通信管道,RegisterInputChannel建立连接。在ViewRootImpl的setView()方法中创设了WindowInput伊芙ntReceiver,并经过WindowManager瑟维斯(Service)(Service)向InputManagerService注册InputChannel监听输入事件。

882828九五至尊手机版 7

882828九五至尊手机版,事件管道连接进度.png

Touch伊夫nt事件传递流程 :

882828九五至尊手机版 8

输入事件传递-服务进程

参考资料:

《Android
的窗口管理连串》

《Android的用户输入处理》
《Android中Motion伊芙(Eve)nt的发源和ViewRootImpl》

Your Comments

近期评论

    功能


    网站地图xml地图