美文网首页
ViewRootImpl简介(一)

ViewRootImpl简介(一)

作者: remax1 | 来源:发表于2020-04-28 10:54 被阅读0次

    关于ViewRootImpl

    ViewRootImpl是view的最高层级,属于所有View的根,但它不是View,实现了viewParent接口,控件的测量、布局、绘制以及输入事件的派发处理都由ViewRootImpl触发,是view和windowManager的通信桥梁。

    构造函数

    老规矩,构造函数入手,看看初始化做了哪些事,在哪里初始化。

      public ViewRootImpl(Context context, Display display) {
            mContext = context;
            /*  从WindowManagerGlobal中获取一个IWindowSession的实例。它是ViewRootImpl和WMS进行通信的代理 */
            mWindowSession = WindowManagerGlobal.getWindowSession();
            mDisplay = display;
            mBasePackageName = context.getBasePackageName();
    
            mDisplayAdjustments = display.getDisplayAdjustments();
            /*创建ViewRootImpl的线程为当前线程。再处理来自控件树的请求时,会检查
              发起请求的线程是否和当前线程相同,不同则会抛出异常*/
            mThread = Thread.currentThread();
            mLocation = new WindowLeaked(null);
            mLocation.fillInStackTrace();
            mWidth = -1;
            mHeight = -1;
            //收集窗口的无效区域
            mDirty = new Rect();
            mTempRect = new Rect();
            mVisRect = new Rect();
            //当前窗口的位置尺寸
            mWinFrame = new Rect();
            /*  创建一个W类型的实例,W是IWindow.Stub的子类。即它将在WMS中作为新         窗口的ID,以及接收来自WMS的回调*/
            mWindow = new W(this);
            mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
            mViewVisibility = View.GONE;
            mTransparentRegion = new Region();
            mPreviousTransparentRegion = new Region();
            mFirst = true; // true for the first time the view is added
            mAdded = false;
            //创建View的内部类,AttachInfo实例,保存了多种信息,窗口实例,viewRootImpl实例等
            mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
            mAccessibilityManager = AccessibilityManager.getInstance(context);
            mAccessibilityInteractionConnectionManager =
                new AccessibilityInteractionConnectionManager();
            mAccessibilityManager.addAccessibilityStateChangeListener(
                    mAccessibilityInteractionConnectionManager);
            mHighContrastTextManager = new HighContrastTextManager();
            mAccessibilityManager.addHighTextContrastStateChangeListener(
                    mHighContrastTextManager);
            mViewConfiguration = ViewConfiguration.get(context);
            mDensity = context.getResources().getDisplayMetrics().densityDpi;
            mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
            mFallbackEventHandler = new PhoneFallbackEventHandler(context);
            mChoreographer = Choreographer.getInstance();
            mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
            loadSystemProperties();
        }
    
       
    

    初始化

    通过代码跟踪发现是在WindowManagerGlobal初始化,去看看里面跟viewRootImpl相关的代码

    public final class WindowManagerGlobal {
        //所有window对象中的view
        private final ArrayList<View> mViews = new ArrayList<View>();
        //所有window对象中对应的viewRootImpl
        private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
        //所有view对象的layoutParams
        private final ArrayList<WindowManager.LayoutParams> mParams =
                new ArrayList<WindowManager.LayoutParams>();
    //···代码省略
    
     public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
        //代码省略```
               ViewRootImpl root;
               View panelParentView = null;
    
                if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                        wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                    final int count = mViews.size();
                    for (int i = 0; i < count; i++) {
                        if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                            panelParentView = mViews.get(i);
                        }
                    }
                }
              
               root = new ViewRootImpl(view.getContext(), display);
    
                view.setLayoutParams(wparams);
                //添加view
                mViews.add(view);
                //添加ViewRootImpl 
                mRoots.add(root);
                mParams.add(wparams);
                 // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            }
    
    }
    }
    
    

    不难看出,所有的view和viewRootImpl都同时加进同一个集合中,且有着顺序对应的关系。

    在ViewRootImpl的构造函数中创建了以下几个主要对象:

    (1) 通过WindowManagerGlobal.getWindowSession()得到IWindowSession的代理对象,该对象用于和WMS通信。

    (2) 创建了一个W本地Binder对象mWindow,用于WMS通知应用程序进程。

    (3) 创建ViewRootHandler对象,用于处理当前视图消息。

    (4) 构造一个AttachInfo对象;

    (5) 创建Surface对象,用于绘制当前视图,当然该Surface对象的真正创建是由WMS来完成的,只不过是WMS传递给应用程序进程的。

    如何关联上

    细心的朋友发现,在介绍WindowManagerGlobal 的addView()方法时,我没有去介绍ViewRootImpl的setView()方法。没错!就是通过这个方法,指定一个控件树管理,并向WMS添加一个新窗口后,ViewRootImpl的通信作用才显现出来。去看看setView里面做了啥事。

      public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    //mView保存了控件的根
                    mView = view;
    
                    mAttachInfo.mDisplayState = mDisplay.getState();
                    mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
    
                    mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
                    mFallbackEventHandler.setView(view);
                    //mWindowAttributes保存了窗口对应的LayoutParams
                    mWindowAttributes.copyFrom(attrs);
                    if (mWindowAttributes.packageName == null) {
                        mWindowAttributes.packageName = mBasePackageName;
                    }
                    attrs = mWindowAttributes;
                    // Keep track of the actual window flags supplied by the client.
                    mClientWindowLayoutFlags = attrs.flags;
    
                    setAccessibilityFocus(null, null);
    
                    if (view instanceof RootViewSurfaceTaker) {
                        mSurfaceHolderCallback =
                                ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                        if (mSurfaceHolderCallback != null) {
                            mSurfaceHolder = new TakenSurfaceHolder();
                            mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                        }
                    }
    
                    // Compute surface insets required to draw at specified Z value.
                    // TODO: Use real shadow insets for a constant max Z.
                    if (!attrs.hasManualSurfaceInsets) {
                        final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
                        attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
                    }
    
                    CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
                    mTranslator = compatibilityInfo.getTranslator();
    
                    // If the application owns the surface, don't enable hardware acceleration
                    if (mSurfaceHolder == null) {
                        enableHardwareAcceleration(attrs);
                    }
    
                    boolean restore = false;
                    if (mTranslator != null) {
                        mSurface.setCompatibilityTranslator(mTranslator);
                        restore = true;
                        attrs.backup();
                        mTranslator.translateWindowLayout(attrs);
                    }
                    if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
    
                    if (!compatibilityInfo.supportsScreen()) {
                        attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                        mLastInCompatMode = true;
                    }
    
                    mSoftInputMode = attrs.softInputMode;
                    mWindowAttributesChanged = true;
                    mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
                    mAttachInfo.mRootView = view;
                    mAttachInfo.mScalingRequired = mTranslator != null;
                    mAttachInfo.mApplicationScale =
                            mTranslator == null ? 1.0f : mTranslator.applicationScale;
                    if (panelParentView != null) {
                        mAttachInfo.mPanelParentWindowToken
                                = panelParentView.getApplicationWindowToken();
                    }
                    mAdded = true;
                    int res; /* = WindowManagerImpl.ADD_OKAY; */
    
                    /*在添加窗口之前,先通过requestLayout()方法遍历。最终是通过调用performTraversals(),这个方法实现了对控件树测量,布局,向WMS申请修改窗口属性以及重绘工作*/
                    requestLayout();
                    if ((mWindowAttributes.inputFeatures
                            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                        mInputChannel = new InputChannel();
                    }
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        /*将窗口添加至到WMS中*/
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } catch (RemoteException e) {
                        ···
                    } finally {
                       ···
                    }
    
                    ···
                    //错误处理,通常是权限问题,重复添加
                    if (res < WindowManagerGlobal.ADD_OKAY) {
                        mAttachInfo.mRootView = null;
                        mAdded = false;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null, null);
                        switch (res) {
                            case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                            case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- token " + attrs.token
                                        + " is not valid; is your activity running?");
                            case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- token " + attrs.token
                                        + " is not for an application");
                            case WindowManagerGlobal.ADD_APP_EXITING:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- app for token " + attrs.token
                                        + " is exiting");
                            case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- window " + mWindow
                                        + " has already been added");
                            case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                                // Silently ignore -- we would have just removed it
                                // right away, anyway.
                                return;
                            case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window " + mWindow +
                                        " -- another window of this type already exists");
                            case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window " + mWindow +
                                        " -- permission denied for this window type");
                            case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                                throw new WindowManager.InvalidDisplayException(
                                        "Unable to add window " + mWindow +
                                        " -- the specified display can not be found");
                            case WindowManagerGlobal.ADD_INVALID_TYPE:
                                throw new WindowManager.InvalidDisplayException(
                                        "Unable to add window " + mWindow
                                        + " -- the specified window type is not valid");
                        }
                        throw new RuntimeException(
                                "Unable to add window -- unknown error code " + res);
                    }
    
                    if (view instanceof RootViewSurfaceTaker) {
                        mInputQueueCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                    }
                    //如果mInputChannel ,则创建mInputEventReceiver 用于接收输入事件。
                    创建时用的是主线程的Looper,即在主线程读取输入事件和触发事件,这是应用程序可以在ouTouchEvent()等事件响应UI操作的根本原因
                    if (mInputChannel != null) {
                        if (mInputQueueCallback != null) {
                            mInputQueue = new InputQueue();
                            mInputQueueCallback.onInputQueueCreated(mInputQueue);
                        }
                        mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());
                    }
                     /* ViewRootImpl将作为参数view的parent。所以,ViewRootImpl可以从控件树中任何一个控件开始,通过回溯getParent()的方法得到 */
                    view.assignParent(this);
               ···
                }
            }
        }
    
    

    总结

    至此,所有的成员变量已经初始完毕,新的窗口也添加进WMS中。ViewRootImpl的创建过程是由构造函数和setView()方法两个步骤完成的。其中构造函数主要进行成员的初始化,setView()则是创建窗口、建立输入事件接收机制的入口。

    相关文章

      网友评论

          本文标题:ViewRootImpl简介(一)

          本文链接:https://www.haomeiwen.com/subject/vuvywhtx.html