我们通常写Activity的时候在onCreate里面都会写上setContentView()这一步,该方法会将你指定的布局文件加载并显示到屏幕中,在其中会经历一系列操作,其中也就涉及到了Activity,Window,DecorView,ViewRootImpl,今天就让我们借助这个方法来看看这些对象之间的关系。

如果你在5.0之前就开始学习android了,那你会发现一个Activity的改变,就是默认创建Activity类时所继承的类不同了,在5.0之前默认继承的是Activity类,在5.0之后默认继承的是AppCompatActivity类。

5.0之前

setContentView是这样的

public void setContentView(int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();
}
public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();
}
public void setContentView(View view, ViewGroup.LayoutParams params) {getWindow().setContentView(view, params);initWindowDecorActionBar();
}

一共有三个构造方法

  1. 布局文件id
  2. View对象
  3. View对象和ViewGroup的布局参数

我们最常用的便是第一个了,首先获取一个window对象然后调用它的setContentView方法,initWindowDecorActionBar();

()会初始化actionBar。

    public Window getWindow() {return mWindow;}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, ActivityConfigCallback activityConfigCallback) {attachBaseContext(context);.............................................................mWindow = new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(this);mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);mWindow.getLayoutInflater().setPrivateFactory(this);...............................................mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);if (mParent != null) {mWindow.setContainer(mParent.getWindow());}mWindowManager = mWindow.getWindowManager();..................................................}

可以看到window对象是在attach方法中被赋值的,它本身是一个PhoneWindow对象,还为其设置了callback等对象

window设置了窗口管理器WindowManager用于管理窗口,使用context.getSystemService(Context.WINDOW_SERVICE)得到WindowManager

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {mAppToken = appToken;mAppName = appName;mHardwareAccelerated = hardwareAccelerated|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);if (wm == null) {wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}public WindowManagerImpl createLocalWindowManager(Window parentWindow) {return new WindowManagerImpl(mContext, parentWindow);}

调用setWindowManager传入WindowManager,在其中还会再次检查WindowManager对象是否是空,最后调用createLocalWindowManager方法创建WinodwManager的实现类

    public PhoneWindow(Context context, Window preservedWindow,ActivityConfigCallback activityConfigCallback) {this(context);.............................if (preservedWindow != null) {mDecor = (DecorView) preservedWindow.getDecorView();mElevation = preservedWindow.getElevation();..............................}...............................}

在PhoneWindow中会给mDecor赋值

    @Overridepublic void setContentView(int layoutResID) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {//构造DecorView对象并赋值给mDecor,并进行mContentParent的初始化installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());transitionTo(newScene);} else {//将Resource对于的id等于layoutResID的xml布局文件,add到mContentParent中mLayoutInflater.inflate(layoutResID, mContentParent);}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}

在PhoneWindow的setContentView中会先对mContentParent对象进行判空,如果为空就创建DecorView,如果有视图的话就清空视图,请求重新Layout,刷新视图

    public void removeAllViews() {removeAllViewsInLayout();requestLayout();invalidate(true);}
private void installDecor() {.......................mDecor = generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);.....................................mContentParent = generateLayout(mDecor);.........................}

installDecor方法很长,这里只贴了主要代码,generateDecor会生成DecorView对象并为其设置Theme,generateLayout的源码超长,主要就是根据Theme的不同加载不同的布局样式,然后再向mContentParent中加入子View

    protected DecorView generateDecor(int featureId) {// System process doesn't have application context and in that case we need to directly use// the context we have. Otherwise we want the application context, so we don't cling to the// activity.Context context;if (mUseDecorContext) {Context applicationContext = getContext().getApplicationContext();if (applicationContext == null) {context = getContext();} else {context = new DecorContext(applicationContext, getContext());if (mTheme != -1) {context.setTheme(mTheme);}}} else {context = getContext();}return new DecorView(context, featureId, this, getAttributes());}

就这样吧一个decorview就建立完成了,下面我们来看看5.0之后的流程是怎样的

5.0之后

首先看AppCompatActivity中的setContentView()方法

    @Overridepublic void setContentView(@LayoutRes int layoutResID) {getDelegate().setContentView(layoutResID);}

getDelegate()得到代理对象

    @NonNullpublic AppCompatDelegate getDelegate() {if (this.mDelegate == null) {this.mDelegate = AppCompatDelegate.create(this, this);}return this.mDelegate;}
    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);}public void setContentView(View v) {this.ensureSubDecor();ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);contentParent.removeAllViews();contentParent.addView(v);this.mOriginalWindowCallback.onContentChanged();}

调用了AppCompatDelegateImpl的setContentView方法,首先会调用ensureSubDecor,在该方法内部会调用createSubDecor来创建SubDecor,并为它设置标题栏。下面的代码很长,重点的部分我在注释中写出来了

private void ensureSubDecor() {if (!this.mSubDecorInstalled) {this.mSubDecor = this.createSubDecor();CharSequence title = this.getTitle();if (!TextUtils.isEmpty(title)) {if (this.mDecorContentParent != null) {this.mDecorContentParent.setWindowTitle(title);} else if (this.peekSupportActionBar() != null) {this.peekSupportActionBar().setWindowTitle(title);} else if (this.mTitleView != null) {this.mTitleView.setText(title);}}。。。。。。。。。。。。。。。。。。。。。。。}}private ViewGroup createSubDecor() {TypedArray a = this.mContext.obtainStyledAttributes(styleable.AppCompatTheme);if (!a.hasValue(styleable.AppCompatTheme_windowActionBar)) {a.recycle();//当使用了AppCompatActivity但是没有设置一个Theme.AppCompat的主题,则会报这个Exceptionthrow new IllegalStateException("You need to use a Theme.AppCompat theme (or descendant) with this activity.");} else {//设置一些Window属性if (a.getBoolean(styleable.AppCompatTheme_windowNoTitle, false)) {this.requestWindowFeature(1);} else if (a.getBoolean(styleable.AppCompatTheme_windowActionBar, false)) {this.requestWindowFeature(108);}if (a.getBoolean(styleable.AppCompatTheme_windowActionBarOverlay, false)) {this.requestWindowFeature(109);}if (a.getBoolean(styleable.AppCompatTheme_windowActionModeOverlay, false)) {this.requestWindowFeature(10);}this.mIsFloating = a.getBoolean(styleable.AppCompatTheme_android_windowIsFloating, false);a.recycle();//在这里创建了DecorViewthis.mWindow.getDecorView();LayoutInflater inflater = LayoutInflater.from(this.mContext);ViewGroup subDecor = null;//根据标志来决定inflate哪个layout 下面好大一片if (!this.mWindowNoTitle) {if (this.mIsFloating) {subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null);this.mHasActionBar = this.mOverlayActionBar = false;} else if (this.mHasActionBar) {TypedValue outValue = new TypedValue();this.mContext.getTheme().resolveAttribute(attr.actionBarTheme, outValue, true);Object themedContext;if (outValue.resourceId != 0) {themedContext = new ContextThemeWrapper(this.mContext, outValue.resourceId);} else {themedContext = this.mContext;}subDecor = (ViewGroup)LayoutInflater.from((Context)themedContext).inflate(layout.abc_screen_toolbar, (ViewGroup)null);this.mDecorContentParent = (DecorContentParent)subDecor.findViewById(id.decor_content_parent);this.mDecorContentParent.setWindowCallback(this.getWindowCallback());if (this.mOverlayActionBar) {this.mDecorContentParent.initFeature(109);}if (this.mFeatureProgress) {this.mDecorContentParent.initFeature(2);}if (this.mFeatureIndeterminateProgress) {this.mDecorContentParent.initFeature(5);}}} else {if (this.mOverlayActionMode) {subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null);} else {subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null);}if (VERSION.SDK_INT >= 21) {ViewCompat.setOnApplyWindowInsetsListener(subDecor, new OnApplyWindowInsetsListener() {public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {int top = insets.getSystemWindowInsetTop();int newTop = AppCompatDelegateImpl.this.updateStatusGuard(top);if (top != newTop) {insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), newTop, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());}return ViewCompat.onApplyWindowInsets(v, insets);}});} else {((FitWindowsViewGroup)subDecor).setOnFitSystemWindowsListener(new OnFitSystemWindowsListener() {public void onFitSystemWindows(Rect insets) {insets.top = AppCompatDelegateImpl.this.updateStatusGuard(insets.top);}});}}if (subDecor == null) {throw new IllegalArgumentException("AppCompat does not support the current theme features: { windowActionBar: " + this.mHasActionBar + ", windowActionBarOverlay: " + this.mOverlayActionBar + ", android:windowIsFloating: " + this.mIsFloating + ", windowActionModeOverlay: " + this.mOverlayActionMode + ", windowNoTitle: " + this.mWindowNoTitle + " }");} else {if (this.mDecorContentParent == null) {this.mTitleView = (TextView)subDecor.findViewById(id.title);}ViewUtils.makeOptionalFitsSystemWindows(subDecor);ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);// 获取PhoneWindow中的content布局对象ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290);if (windowContentView != null) {while(windowContentView.getChildCount() > 0) {View child = windowContentView.getChildAt(0);windowContentView.removeViewAt(0);contentView.addView(child);}windowContentView.setId(-1);// 设置contentView的id 这里的数字在R文件中可以查出来是R.id.contentcontentView.setId(16908290);if (windowContentView instanceof FrameLayout) {((FrameLayout)windowContentView).setForeground((Drawable)null);}}this.mWindow.setContentView(subDecor);contentView.setAttachListener(new OnAttachListener() {public void onAttachedFromWindow() {}public void onDetachedFromWindow() {AppCompatDelegateImpl.this.dismissPopups();}});return subDecor;}}}

getDecorView里面就和之前写的一样了

小结:

Activity的具体实现都会转交给它里面的Window来实现,调用了 PhoneWindow的setContentView首 先会查看DecorView存不存在,不存在的话则创建一个DecorView出来,但是此时的DecorView还只是空白的,需要通过 generateLayout方法加载具体的布局到DecorView上面,具体的布局文件的话可能和系统版本以及主题有关系,随后会通过 LayoutInflater的inflate通过pull解析xml的方式将我们自己的布局文件添加到DecorView里面的 mContentParent中,注意只是添加到mContentParent中, 接着回调Activity的onContentChanged方法,告诉Activity DecorView已经初始化好了,即Activity的布局文件已经成功的添加到了DecorView的mContentParent里面啦,但是这个时候DecorView还是没有被WindowManager添加到Window上面的,因为View不能单独存在,所以我们需要将DecorView添 加到Window里面。

Activity.attach()的执行

首先启动一个Activity会调用handleLaunchActivity

private void handleLaunchActivity (ActivityClientRecord r, Intent customIntent){//会在里面调用Activity.attach,并调用onCreate,和onstartActivity a = performLaunchActivity(r, customIntent);.....................................if (a != null) {//执行Activity.onResume()handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed);if (!r.activity.mFinished && r.startsNotResumed) {try {r.activity.mCalled = false;//执行Activity.onPause()mInstrumentation.callActivityOnPause(r.activity);}}}}
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {//调用ctivity.onResume()已经调用了,但是现在界面还是不可见的ActivityClientRecord r = performResumeActivity(token, clearHide);if (r != null) {final Activity a = r.activity;if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();//decor对用户不可见decor.setVisibility(View.INVISIBLE);ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;if (a.mVisibleFromClient) {a.mWindowAdded = true;//将decorview添加进WindowManagerwm.addView(decor, l);}if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {//使DecorView可见if (r.activity.mVisibleFromClient) {r.activity.makeVisible();}}}}
}

addView()

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

WindowManagerGlobal是WindowManager的实现类

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;...............................synchronized (mLock) {ViewRootImpl root;//实例化一个ViewRootImpl对象root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);}...........................try {//将DecorView交给ViewRootImplroot.setView(view, wparams, panelParentView);} catch (RuntimeException e) {}
}

WindowManagerGlobal中有三个列表,一个是保存View的mViews列表,一个是保存ViewRootImpl的mRoots列表,一个是保存WindowManager.LayoutParams的mParams列表,在调用setView后将decorView交给ViewRootImpl,ViewRootImpl中调用performTraversals方法,然后便开始测量布局绘画了,界面才得以显示出来。

总的来说addView干了4件事:

  1. 首先进行了一些输入参数的检查,不合法的话抛出相应异常信息
  2. 接着创建了一个 ViewRootImpl对象,在创建ViewRootImpl的时候会创建一个Session对象出来,而Session对象的创建是通过调用的 WindowManagerService的openSession方法了
  3. 随后会将我们当前的ViewRootImpl对象放到mRoots列 表中,将当前的View对象放到mViews中,将当前的LayoutParams对象放到mParams中
  4. 最后调用ViewRootImpl 的setView方法,这个方法里面真正的进行View添加操作是通过创建ViewRootImpl的时候创建的Session对象完成的,而在 Session中添加操作实际上是通过WindowManagerService完成的,后期的添加操作是有涉及到IPC通信的

总结

每一个Activity有一个Window,每一个Window都有一个DecorView,而每一个View在被添加的时候都会创建一个ViewRootImpl,但是Window所在的ViewRootImpl应该是属于DecorView这个View的,其实ViewRootImpl我们可以认为是View的操作者一样,因为事件分发机制,View视图的绘制都是从他开始的每一个Activity有一个Window,每一个Window都有一个DecorView,而每一个View在被添加的时候都会创建一个ViewRootImpl,但是Window所在的ViewRootImpl应该是属于DecorView这个View的,其实ViewRootImpl我们可以认为是View的操作者一样,因为事件分发机制,View视图的绘制都是从他开始的

Activity、Window、DecorView、ViewRootImpl之间的关系相关推荐

  1. android类之间的关系,Android 中Activity,Window和View之间的关系

    Activity是Android应用程序的载体,允许用户在其上创建一个用户界面,并提供用户处理事件的API,如 onKeyEvent, onTouchEvent等. 并维护应用程序的生命周期.Acti ...

  2. android 如何获得activity的view对象,Android的Activity 、 Window 、 View之间的关系

    什么是Activity .View . Window? Activity:是Android 四大组件之一, 是存放View对象的容器,也是我们界面的载体,可以用来展示一个界面.它有一个SetConte ...

  3. 深入理解WMS(三):剖析Activity,View,Window之间的关系

    这篇课程开头就说在"接触 Android 开发时,我始终认为它就是负责将 layout 布局中的控件渲染绘制出来的".的确,对于layout布局怎么跟Activity关联起来的,都 ...

  4. 【Android 界面效果10】Android中View,ViewGroup,Window之间的关系

    一.首先说说View和ViewGroup吧 Android系统中的所有UI类都是建立在View和ViewGroup这两个类的基础上的.所有View的子类成为"Widget",所有V ...

  5. 一篇文章看明白 Android 图形系统 Surface 与 SurfaceFlinger 之间的关系

    Android - SurfaceFlinger 图形系统 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 一篇文 ...

  6. 文化袁探索专栏——Activity、Window和View三者间关系

    文化袁探索专栏--Activity.Window和View三者间关系 <文化袁探索专栏--View三大流程#Measure 文化袁探索专栏--View三大流程#Layout 文化袁探索专栏--H ...

  7. Android中Activity、Window、ViewRootImpl与子线程更新UI

    三者层级关系 1.Window Window是一个抽象类,唯一的实现类是PhoneWindow Window分为三种类型应用Window.子Window.系统Window.子Window无法独立存在必 ...

  8. 深入理解WMS(四):从WMS的角度分析Activity之间的关系

    system_server_AMS类图.png Activity_Window_相关类图.png system_server_AMS类图.png 总体类图.png 流程图.png 一.相关概念 1.A ...

  9. X Window、GNOME和KDE之间的关系

    X Window.GNOME和KDE之间的关系 分类: Linux2011-04-25 23:06 3570人阅读 评论(1) 收藏 举报 桌面环境文件管理器unix图形linuxqt 目录(?)[+ ...

最新文章

  1. 如何读取Excel表格中不同sheet表的同一位置单元格数据,并绘制条形图呢?
  2. c语言程序设计迷宫,C语言程序设计课程设计-迷宫.doc
  3. jzoj1252,P5194-天平【dfs,set】
  4. ftp网页版服务器地址,免费ftp服务器地址汇总
  5. 使用 SYSENTER 和 SYSEXIT 指令执行对系统过程的快速调用
  6. mysql读写分离6_6\MySQL 主从同步 、 MySQL 读写分离 、 MySQL 性能调优
  7. 【ML】理解偏差和方差,过拟合和欠拟合
  8. 使用PDman进行数据库设计
  9. RH236介绍红帽GLUSTER存储
  10. 使用python打印九九乘法表
  11. jmf608硬盘修复_JMF608固态硬盘主控 SSD套料 SSD电路板 SATA3 封装BGA152|TSOP48
  12. 单机Eureka构建步骤
  13. 433模块-----HCS301芯片烧录器
  14. error execution phase preflight: couldn‘t validate the identity of the API Server: Get
  15. zoj 2334 Monkey King
  16. 博链财经专访MDUKEY创始人韩元桢
  17. 剧本翻译之SHUFFLE 6月21日
  18. 面试宝典之高分回答面试题(一)
  19. 数论——余数相关定理
  20. centos终端下使用dvorak键盘布局

热门文章

  1. 六种常见系统架构 —— 进阶篇
  2. 守卫“舌尖上的安全”,极视角与紫光华智共建“AI+明厨亮灶”
  3. Linux大神进阶十五:磁盘管理
  4. Autocad2008 32位 安装到64位电脑上的方法
  5. CVTE硬件岗面试经历
  6. 【web前端】近期题目汇总
  7. 期刊印刷时间大概多久
  8. iText 学习(四)
  9. 为什么要使用 TCP keepalive?C/C++代码实现
  10. PCIE Connector Pinout