View的面试锦囊

2020-10-12  本文已影响0人  dashingqi
Android_Banner.jpg

View的draw流程

View.getLocationInWindow()和View.getLocationOnScreen()区别

getLocationInWindow

getLocationOnScreen

首次View的绘制是发生在什么时候

是在ActivityThread的handleResumeActivity()方法中

流程分析
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,                                   String reason) { 
if (r.window == null && !a.mFinished && willBeVisible) {
                    // 获取到Window对象
            r.window = r.activity.getWindow();
                    //获取到DecorView
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
                    //获取到WindowMananger对象
            ViewManager wm = a.getWindowManager();
             ........ 
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    /**
                     * 重点中的重点
                     * 调用WindowManager的addView(还记得 WindowManager是一个接口类吧,唯一实现子类是WindowManagerImpl)
                     * 其实调用了WindowManger的addView()方法就是将DecoeView添加到了WindowManagerService中了
                     * (PhoneWindow只是处理一些应用窗口通用的逻辑(设置标题栏、导航栏))
                     * 去看下WindowManagerImpl的addView()
                     */
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }
}
  
// WindowManagerImpl # addView()
  
  @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        /**
         * mGlobal ----> WindowManagerGlobal # addView
         */
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
  // 在这里委托给WindowManagerGlobal 调用了它的addView()方法 
  
  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
                    ....... 省略代码

            //创建了ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            // view ---> DecorView 设置布局参数
            view.setLayoutParams(wparams);

            //将添加的View保存到View列表中
            mViews.add(view);
            // 将root保存到ViewRootImpl的列表中
            mRoots.add(root);
            // 将wparams参数保存到mParams列表中
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                /**
                重点
                 * root 是ViewRootImpl的对象
                 * 调用了ViewRootImpl的setView()
                 * 将DecorView添加到WMS
                 * 将窗口和窗口参数设置到ViewRootImpl中
                 */
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
// 以上主要保存了一些信息。并且将View和布局参数传入到了ViewRootImpl的setView中

//ViewRootImpl # setView()
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {

                //这里传递进来的view其实是创建好的DecorView
                mView = view;
                                .......省略代码
                /**
                 * 重点呀
                 * 在将View添加到WindowManagerService中之前,确保View进行了一次 measure-->layout->draw
                 * 调用了此方法后,与ViewRootImpl关联的View会执行一次measure-->layout->draw
                 */
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    /**
                     * 重点分析
                     */
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    setFrame(mTmpFrame);
                } 

                    ......省略代码
                                // 这里这个iew 是DecorView,把ViewRootImpl作为它的父亲设置给DecorView
                // 这也是DecorView与ViewRootImpl之间的关系
                view.assignParent(this);  
        }
    }

ViewRootImpl的创建时机在什么时候

正如“View首次绘制时机时的分析”,ViewRootImpl的创建时机是在View首次绘制时,是在调用WindowManagerGlobal的addView()的方法中进行创建的。

ViewRootImpl与DecorView的关系

ViewRootImpl 是作为DecorView的父亲

在ViewRootImpl的setView()方法中 -----> view.assignParent(this);

ViewRootImpl实现了ViewParent的接口

DecorView的布局是怎么样的

DecorView的创建时机

在上述分析过程中,在PhoneWindow的installDecor()方法中有调用到generateLayout(-1)创建了DecorView

这是DecorView创建的地方。

installDecor() 调用的时机是从setContentView()作为入口的,执行的流程就是 Activity(setContentView) ---> PhoneWindow(setContentView) ----> installDecor()

所以DecorView的创建时机是setContentView()方法的执行

setContentView()干了什么

layoutInflate的流程是什么

https://www.jianshu.com/p/cc5db309cdcf

PhoneWindow的创建时机

如何触发View的重新绘制呢?

requestLayout 和 invalidate的区别

区别和联系

点击事件中开启一个子线程更新UI会怎么样?

 btn_toast.setOnClickListener { listener ->
            //Toast.makeText(this, "展示 吐司", Toast.LENGTH_SHORT).show()
            Thread(Runnable {
                var name = Thread.currentThread().name
                Log.d("tag", "$name")
                btn_toast.text = "子线程"
            }).start()
        }

这个问题分两种情况,控件宽高固定,控件高度固定宽度warp_content,

情况1
情况2

从Activity创建到View的显示这个过程中主要都发生了什么?

这个问题很广泛,我个人认为可以拆分成以下几点回答

handleLaunchActivity

在handleLaunchActivity方法主要调用了performLaunchActivity() 和 handleResumeActivity()

setContentView()过程

调用流程是 Activity(setContentView) ---> PhoneWindow(setContentView) ---> installDecor()

handleResumeActivity()方法的执行过程
上一篇 下一篇

猜你喜欢

热点阅读