Activity加载view6.0源码分析---setContentView

本篇博文介绍三个方面的知识

chapter One:认识Activity的布局

chapter Two:启动activity时的布局--从setContentView说起

chapter Three:总结说明

View这个东西,是构成页面Activity最基本的元素,所以可想而知他到底有多重要。在研究activity组件的view加载之前,先整体认

识下activity的布局,有助于更好的去理解setContentView方法

Chapter One:认识Activity的布局

对于研究布局这种东西,必须要掌握一些视图工具,在这里推荐一个sdk查看视图的工具sdk\tools\hierarchyviewer,随意找一个界

面去查看activity的view视图

在这个activity界面中我把导航栏给隐藏了,所以不存在导航栏,根据这张图的话大致可以看到一个activity的布局,再结合对

setContentView的研究,可以总结出activity的布局图如下:

从这张activity的布局图可以看到:一个activity对应一个应用窗口mWindow,应用窗口mWindow包括activity的顶级view是

mDecorView,mDecorView包括状态栏statusbar和导航栏navigationbar以及要加载activity布局的view-----------------------

mDecorContentParent,该view又包括一个标题栏titlebar和activity的内容布局contentparent。在contentParent中就是该

activity的view树。

  1. mWindow:Window对象,Window是一个抽象类,是activity的顶层外观和行为的代理。会往windowmanage中添加该类的一个实例作为顶层view。window提供基本的UI代理,比如背景啊,标题区域啊,按键处理啊等等,Window只有一个实现类PhoneWindow,所以mWindow对象实际是PhoneWindow对象。当启动一个activity的过程中会初始化一个属于Phonewindow的window对象。Phonewindow对象的创建在activity的attach方法中
  2. mDecor:DecorView对象,继承自framelayout,是window窗口的 顶级view,包含window的装饰。类的定义位于PhoneWindow.java中
  3. mDecorContentParent:DecorContentParent对象,实现类是ActionbarOverlayLayout,属于activity布局的最外层view,包括标题栏和activity的内容布局
  4. mContentParent:activity的内容布局,继承自ViewGroup,用来加载存放activity的view树,如果没有标题栏,那么mContentparent的大小回合mDecorContentParent相等,以此类推
  5. 状态栏:statusbar,对应的id为statusBarBackground,在PhoneWindow中会加载,当window属性发生改变时会刷新导航栏。但不论是导航栏和状态栏,从这个id也可以看出,PhoneWindow只是加载他们的background,即相当于只加载一个view的占位,先告诉应用窗口,系统窗口要求将状态栏和导航栏布局在这里,你不要占用,但此时不会加载导航栏和状态栏的view,只是绘制背景而已
  6. 导航栏:navigationbar,对应的id为navigationBarBackground,在PhoneWindow中会加载,当window属性发生改变时会刷新状态栏
  7. 标题栏:titlebar,对于导航栏,状态栏和标题栏的存在与否,与window的属性特征有关,在加载view时所以会去判断window的属性特征,进而决定是否要加载这三者

对activity的布局大致有个了解之后,就开始去分析activity启动后加载view的流程

Chapter Two:activity加载view布局----始于setContentView

         对于activity的布局的加载大致分为两部分,一部分是加载view,另一部分是将view绑定到应用窗口Window。其中这两个步

骤中将view绑定到window是在启动activity时完成的操作,是将mDecor绑定到window。然后再往mDecor中添加各种view。对于

activity的启动过程留待以后进行分析,现在分析加载view---始于Activity.java的setContentView方法,看一下加载view的流程。

可以看到代码流程很简单,从Activity.java的setContentView方法进入,到PhonewWindow.java的setContentView方法进行一系

列处理,接下来进入代码进行分析

1,Activity.java的setContentView方法,代码路径\android\frameworks\base\core\java\android\app

 public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

源码中对该方法的解释是,从一个layout文件中取出view设置成activity的content,该资源文件会被填充,并遍历文件中的所有

view添加到activity。意思就是填充一个资源文件,加载view。做了两件事儿

  • 一是getWindow获取到Window对象,然后去调用Window的setContentView方法。
  • 二是initWindowDecorActionbar(),创建actionbar对象,填充mDecor下的actionbarView,并把view加载上去(博主猜测是在Window的setContentView方法中只是填充一个actionbar的占位,然后initWindowDecorActionbar()完成view的加载)

重点研究第一步:getWindow().setContentView方法。

首先一个问题,为什么我要说getWindow.setContentView调用的是PhoneWindow中的setContentView方法??

解疑:查看getWindow方法

 public Window getWindow() {
        return mWindow;
    }

返回的是activity的mWindow对象,对于mWindow对象的创建也是在Activity.java中的attach方法中

    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) {


                    。。。。
                    。。。。。。。
                   。。。。。
                   。。。。
                  //创建mWindow对象

           mWindow = new PhoneWindow(this);
                //设置mWindow回调为该activity,这个在接下来setContentView的分析中会触发回调
                 mWindow.setCallback(this);


}

同时,进入到Window.java中也可以看到这一点:

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 */
public abstract class Window {

源码中对于Window类的说明是:

Window是一个抽象类,是最顶层的窗口的外观和行为的代理,window的实例应该被作为最顶层的UI添加到WindowManage

中。Window提供了基本的ui,比如背景,标题区域,默认的按键处理过程等等。Window只有一个唯一的实现类PhoneWindow,

当需要Window对象时需要去初始化PhoneWindow。

至此,对于Activity中的mWindow对象大致有了一个清晰的认识了:他是个PhoneWindow对象,Window.java中方法的实现在

PhoneWindow.java中

2,PhoneWindow.java中的setContentView方法,代码路径\android\frameworks\base\core\java\com\android\internal\policy

 @Override
    public 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对象和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 {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            //通知activity内容已经改变,触发onContentChanged方法
             cb.onContentChanged();
        }
    }

先来总结一下代码的流

在新启动一个activity时mContentParent还未绑定id,此时mContentParent为null。从代码流程图中可以看出setContentView做了三件事 installDecor实例化DecorView对象和mContentParent对象 填充layout文件 通知activity布局已经改变 为什么说是通知activity布局已经改变呢?这是因为在Activity.java的attach方法中mWindow对象设置了callback为this,所以在getCallback时获取到的cb为当前与该window对应的activity。 3,PhoneWindow.java中的installDecor方法分析-----实例化DecorView对象和mContentParent对象

private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
                    ...............
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            
                }
            }

            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
           }

。。。。。。。。
。。。。。。。。 }
    }

在创建一个activity时mDecor和mContentParent均为null

  • 借助generateDecor方法实例化mDecor,即获取到activity的最顶级的view
  • 借助generateLayout方法实例化mContentParent对象,并且根据window的不同Feature来选择对应的布局文件(总之,generatelayout其实就是根据当前的window的特征属性feature来加载内容布局,并获取到当前布局的最外层view。也就是说
  • generatelayout本质就是根据activity的theme主题来找到对应的xml布局并且获取到id为content的ViewGroup赋给mContentParent)
  • 获取到mDecorParent对象,并且根据getLocalFeature获取到的Feature来设置(这也就说明了在自定义Activity时为什么要将
  • getWindow.requestFeature方法卸载setContentView方法之前)
  • 对title进行隐藏或者是设置内容的操作
  • 如果需要切入切出动画,那么就获取到各种动画资源

接下里对installDecor中某些代码做一些分析

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

用于对焦点的传递设置:只有当子view不想获取焦点时mDecor才会去获取焦点

mDecor.postOnAnimation(mInvalidatePanelMenuRunnable)

去开启初始化menu菜单的线程

在这里说明一句,为什么requestFeature要写在setContentView前面,这是因为在调用setContentView时会获取到window的各种

feature进行一些判断设置。

4,PhoneWindow.java中的generateLayout方法研究

第一步:首先是获取到window的布局style

     TypedArray a = getWindowStyle();

第二步,获取到各种属性并进行requestFeature的设置

第三步,通过获取到的window的布局去获取window的各种属性,并根据window的各种属性去选择不同的layout的文件,比如标题栏

是否隐藏,window是悬浮窗还是全屏,等等问题。当然因为在3.0和4.0以及5.0对于menukey的支持不同,所以会有一个与版本相关

的 一个判断。至于这个版本之间有什么不同可以参考总结说明中列出来的文件。

其实generatelayout就做了一件事,那就是根据window的各种属性去获取不同的xml文件。

Chapter Three,总结说明

  • setContentView执行流程中主要涉及到3个类PhoneWindow.java,Activity.java和Window.java
  • Window和windowmanager中的各种feature和flag的style对应的各种含义以及动画style在\android\android\frameworks\base\core\res\res\values\attrs.xml文件中有注释说明
  • 在menu键的设置中涉及到了版本问题,包括3.0,4.0和5.0分别有对应的不同处理,参考\android\android\frameworks\base\core\java\android\os\Build.java可以看到注释有说明各版本有什么不同
  • 至于为什么说mDecor是最外层view,是因为在generateLayout方法中mDecor将填充该xml文件的view--mContentRoot添加了进来。

Activity在启动加载布局共有两个操作

  • 创建DecorView的布局:setContentView的流程基本是用来创建DecorView的布局
  • 将布局添加到window窗口:在Activity的启动过程中,会将应用窗口添加到WindowManager中进行统一管理,以及绑定DecorView
  • 对于状态栏和导航栏,是在每次window属性发生变化时会去更新,但是只是设置了一个背景色,只是占位用,没有加载这些view

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mukekeheart的iOS之旅

iOS学习—— UINavigationController的返回按钮与侧滑返回手势的研究

侧滑返回手势是从iOS7开始增加的一个返回操作,经历了两年时间估计iPhone用户大部分都已经忽略了屏幕左上角那个碍眼的back按钮了。之前在网上搜过有关侧滑...

8606
来自专栏程序员互动联盟

【Windows编程】系列第六篇:创建Toolbar与Statusbar

上一篇我们学习了解了如何使用Windows GDI画图,该应用程序都是光光的静态窗口,我们使用Windows应用程序,但凡稍微复杂一点的程序都会有工具栏和状态栏...

4583
来自专栏androidBlog

使用ViewDragHelper打造属于自己的DragLayout(抽屉开关 )

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details...

1242
来自专栏Android 技术栈

Android 关于WebView全方面的使用(项目应用篇)

WebView的使用已经是老生常谈了,看到很多文章说了用法,但我很少看到全的或者是项目中可以直接使用的,都是看了很多后,自己把功能都集合在一起。这里是一份比较全...

1924
来自专栏高性能服务器开发

用两张图告诉你,为什么你的App会卡顿?

有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? 知道Android究竟是如何在屏幕上显示我们期望的画面的? 对A...

7568
来自专栏学海无涯

Android开发之高德地图实现定位

在应用开发中,地图开发是经常需要使用的“组件”,Google Map虽然有官方教程,无奈用不起来,原因你懂的~~那么国内比较出名的是就是百度地图和高德地图,由于...

3974
来自专栏developerHaoz 的安卓之旅

知乎 Matisse 源码解析,带你探究高效图片选择库的秘密

可以看到 Matisse 的可拓展性是非常强的,不仅可以自定义我们需要的主题,而且还可以按照需求来过滤出我们想要的文件,除此之外,Matisse 采用了建造者模...

4211
来自专栏Android源码框架分析

Android窗口管理分析(1):View如何绘制到屏幕上的主观理解

1864
来自专栏Jaycekon

Phantomjs+Nodejs+Mysql数据抓取(2.抓取图片)

概要 这篇博客是在上一篇博客Phantomjs+Nodejs+Mysql数据抓取(1.抓取数据) http://blog.csdn.net/jokerko...

3535
来自专栏张善友的专栏

JQuery相关资料

将SEO,WEB标准与AJAX进行到底 - JQuery(翻译+学习总结) jQuery——JavaScript冲击波 《15天漫游jQuery》 小试牛刀——...

2118

扫码关注云+社区

领取腾讯云代金券