分析View的第一次绘制是在哪里完成的

xiaoxiao2021-02-27  290

先分析Activity的setContentView()

private Window mWindow; public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } public Window getWindow() { return mWindow; }

Activity的setContentView()调用了Window的setContentView()方法 , 接着跟进

public abstract void setContentView(@LayoutRes int layoutResID);

The only existing implementation of this abstract class is android.view.PhoneWindow Window是个抽象类 , 根据Window类的注释可以知道 , Window类的子类只有一个叫做PhoneWindow , 那么看一下PhoneWindow的setContentView()方法 .

ViewGroup mContentParent; /**省略过后的代码*/ @Override public void setContentView(int layoutResID) { if (mContentParent == null) { //先看下面的installDecor()方法的作用 installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } //我们既然知道了mContentParent就是DecorView中的一个FrameLayout , 那么这段代码就是 //解析我们传入的layoutResID之后 , 并添加到mContentParent当中 //如果有不熟悉inflate()方法的 , 可以自己去看一下 , 他最后会调用rInflate()方法后 , 也是 //执行addView操作来添加子View mLayoutInflater.inflate(layoutResID, mContentParent); } private void installDecor() { if (mDecor == null) { //mDecor就是DecorView , 当mDecor为空时 , generateDecor()方法中判断了applicationContext的是否存在等一些列之后 , new了一个DecorView . mDecor = generateDecor(-1); } else { //当mDecor 不为空时将this赋值给了DecorView 的mWindow成员变量. mDecor.setWindow(this); } if (mContentParent == null) { //generateLayout()中判断了很多种情况 , 包括调用requestWindowFeature 时的标记位 , 或者主题等等 , 所以想要调用requestWindowFeature()去掉标题栏时 , 就需要在setContentView()之前调用 . //generateLayout根据情况拿到一个系统的布局文件id之后 , 会调用DecorView的onResourcesLoaded()方法之后再执行addView()将系统的布局文件添加到DecorView之中 //在调用DecorView的findViewById()传入com.android.internal.R.id.content , 获取并返回布局文件中的FramLayout mContentParent = generateLayout(mDecor); } }

installDecor()方法太长 , 只贴几行代码 , 在重新屡一下顺序 .

判断DecorView是否为空 , 如果为空 , generateDecor()方法会返回一个new DecorView()mContentParent其实是一个ViewGroup , 也就是我们在Activity当中setContentView()所传入的布局的父布局 . 要证实这一说法 , 需要看generateLayout()方法该方法中判断了N多种情况 , 获取了一个布局文件的id(例如com.android.internal.R.layout.screen_simple)并把该id传入到mDecorView的onResourcesLoaded()方法当中onResourcesLoaded()中解析布局文件id , 并调用mDecorView的addView()方法成为了mDecorView的子View只有调用mDecorView.findViewById()传入固定的com.android.internal.R.id.content , 获取并赋值给mContentParent

那么看到这里我们知道了在Activity当中的setContentView()方法 , 会成为decorView的子View , 那么一个Activity的mWindow从何而来 , 并且decorView是如何添加到mWindow上的.

这个要从ActivityThread类开始说起 , 首先是ApplicationThread的 scheduleLaunchActivity()方法 . scheduleLaunchActivity() 利用handler()传过去消息 . 调用 handleLaunchActivity()方法 handleLaunchActivity()方法中调用了performLaunchActivity()的方法 performLaunchActivity()方法中调用了Instrumentation的newActivity()方法通过反射返回一个Activity实例 接着调用 activity的attatch()方法传过去一堆参数

scheduleLaunchActivity() ->handleLaunchActivity() -> performLaunchActivity() -> Instrumentation.newActivity() -> activity.attatch()

final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window){ //这里new了一个PhoneWindow赋值给mWindow mWindow = new PhoneWindow(this, window); //这个方法其实就是赋值给mWindow的windowManager赋值 mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), //这个成员变量关注下 , 下面马上就要用到了 mWindowManager = mWindow.getWindowManager(); }

到了这里我们知道了mWindow是在调用Activity的attach()方法时new的一个PhoneWindow

接下来看看ActivityThread的handleResume()方法的一段代码

//这里r.activity可以看成是当前的activity final Activity a = r.activity; r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); wm.addView(decor, l);

最后调用了WindowManager的addView()方法 , 将decorView和LayoutParams 给传过去了 , 而这个WindowManager是从activity中获取的 .

public WindowManager getWindowManager() { return mWindowManager; }

返回的是成员变量mWindowManager , 那根据上面attach()方法 mWindowManager是从mWindow中获取的 .

public WindowManager getWindowManager() { return mWindowManager; }

返回的是Window类中的成员变量mWindowManager , 其实就是activity的attach()方法中setWindowManager()方法中调用了mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);来赋值

public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); }

createLocalWindowManager()方法其实就是new了一个WindowManagerImpl . 到这里终于知道windowManager是从哪里来了 . 那么看看他的addView()方法

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); }

调用了mGlobal的addView()方法

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... ViewRootImpl root = new ViewRootImpl(view.getContext(), display); root.setView(view, wparams, panelParentView); }

这里实例化ViewRootImpl之后调用了setView()方法

/**ViewRootImpl实现了ViewParent*/ public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks { 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); requestLayout(); view.assignParent(this); } @Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } } }

在这茫茫的代码当中 , 只有这两行代码大概明白其意思 , addToDisplay() , 从方法名中看来可能是将window显示到我们的屏幕上 , 能看明白的两行代码中 , 首先看看requestLayout() , ViewRootImpl的这个方法 , 调用了scheduleTraversals()方法 , 在调用doTraversal()方法重新onMeasure 、onLayout和onDraw . 在看看view的assignParent()方法

//给mParent赋值 void assignParent(ViewParent parent) { if (mParent == null) { mParent = parent; } else if (parent == null) { mParent = null; } else { throw new RuntimeException("view " + this + " being added, but" + " it already has a parent"); } }
转载请注明原文地址: https://www.6miu.com/read-3464.html

最新回复(0)