Android setContentView()源码解析

前言

在Activity中一般第一句就是调用setContentView(R.layout.XXX),但这其中系统做了那些工作?

我们知道,在ClassLoader装载了MainActivity之后,首先创建了Application,之后依次调用Application对象的onAttach和onCreate()方法。然后顺序调用第一个Activity的onAttach和onCreate()方法。大概有个印象即可,后文会涉及到。具体参考:Launcher启动应用程序流程源码解析。

新建测试工程TestHierarchy

新建工程后,activity_main.xml中默认只有一个RelativeLayout,其中包含一个TextView。

设置android:id="@+id/myRelativeLayout"android:id="@+id/myTextView"

设置MainActivity继承自Activity。

保持默认MainActivity extends AppCompatActivity

保持默认<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

至此初始工作完毕。

使用hierarchy查看布局结构

hierarchy是随着SDK发布的一款可视化布局分析工具。这里只需要基础的查看布局层次。由于Hierarchy Viewer只能连接Android开发版手机或是模拟器,所以我们先在虚拟机上运行程序,然后进入..\sdk\tools,找到hierarchyviewer.bat,双击。接着选中我们的程序,之后点击Load View Hierarchy,之后会得到一个黑不溜秋的视图。而这里,就是重点要看的地方。但是为了方便理解,省去一部分不必要的Tiltle等元素,这里以继承Activity为例进行解析。可以先点击各个节点试试每个View对应的位置。

MainActivity继承Activity

这里注意下右上角的两个节点,这明显就是activity_main.xml。不信看id!

源码解析

源码位置:frameworks/base/core/java/android/app/Activity.java

Activity#setContentView()

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

由于继承的是Activity,首先我们就省略了initWindowDecorActionBar()这一步。Activity大法好~。接下来要关注的就一行代码。首先看下这个getWindow()返回的是个什么鬼。

    public Window getWindow() {
        return mWindow;
    }

前言中说Activity的创建的时候第一个执行的方法就是attach()。这里的mWindow就是在attach()方法中被实例化的。

    final void attach(...){
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
    }

mWindow是个Window对象,但是PhoneWindow继承于Window。通过PhoneWindow获取到mWindow之后设置了一个回调。Activity实现了Window.Callback接口,而且Activity中持有一个Window的引用,这就意味着在调用Callback接口方法的时候,Activity可以得到相应的回调。并且Activity可以通过Window属性去操作View。跟进getWindow().setContentView(layoutResID)

源码位置:frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

PhoneWindow#setContentView()

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        // 返回false,执行else分支
        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()) {
            cb.onContentChanged();
        }
    }

先大概分析这段代码流程,首先判断mContentParent是不是为空,第一次进来什么也没干呐,铁定为null。FEATURE_CONTENT_TRANSITIONS属性用于设置Activity的切换效果,默认false。上面首先调用了installDecor(),从上下文名称来看,这个方法应该和mContentParent变量有关系。接着调用mLayoutInflater.inflate(layoutResID, mContentParent)将我们设置的R.layout.XXX填充到mContentParent。结合前面hierarchyviewer图来看,mContentParent就是包含activity_main.xml的FrameLayout的一个实例。最后回调Callback#onContentChanged(),这里的cb其实就是Activity对象。这个方法在Activity中的实现为空方法,所以我们可以在自己的Activity中复写这个方法,实现自己的逻辑。在Activity的布局文件发生改动,即调用setContentView()或者addContentView()之后会调用onContentChanged()方法。跟进installDecor(),下面重点解析mDecor和mContentParent。

PhoneWindow#installDecor()

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

首先调用generateDecor()方法获取mDecor实例,接着依据mDecor实例获取到mContentParent。跟进。

PhoneWindow#generateDecor()

    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

    private final class DecorView extends FrameLayout

在PhoneWindow#generateDecor()中直接new了一个DecorView 对象,可以看到:DecorView也只是个继承FrameLayout的ViewGroup。下面跟进generateLayout()。

PhoneWindow#generateDecor()

    protected ViewGroup generateLayout(DecorView decor) {
        // 获取自定义属性window
        TypedArray a = getWindowStyle();
        ...
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        }
        ...
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        }
        ....
        else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }
        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ...
        mDecor.finishChanging();
        return contentParent;
    }

前面省略的一大段的作用是获取自定义属性window之后所做的各种初始化工作,这里以requestFeature(FEATURE_NO_TITLE)为例。因为在这之后才执行View in = mLayoutInflater.inflate(layoutResource, null),将系统依据style采用的布局文件转换为View in,这里继承的是Activity,style=Theme.AppCompat.Light.DarkActionBar,所以加载的布局为screen_title.xml。之后将in加入到mDecor中,接着将in赋值给mContentRoot。这里的public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content。布局文件screen_title.xml如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

id为content的Fragment是contentParent,最外层的LinearLayout为mContentView。id为action_mode_bar_stub的android:visibility="gone"。最后放出一张自己标注的图~

更多Framework源码解析,请移步 Framework源码解析系列[目录]

时间: 2024-10-14 00:32:06

Android setContentView()源码解析的相关文章

Android xUtils3源码解析之注解模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 初始化 public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { su

Android xUtils3源码解析之数据库模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 配置数据库 DbManager.DaoConfig daoConfig = new DbManager.DaoConfig() .setDbName("test.db") .setDbVersion(1) .setDbOpenListe

Android设计模式源码解析之观察者模式

Android设计模式源码解析之观察者模式 本文为 Android 设计模式源码解析 中 观察者模式 分析 Android系统版本: 2.3 分析者:Mr.Simple,分析状态:未完成,校对者:Mr.Simple,校对状态:未开始 1. 模式介绍 模式的定义 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新. 模式的使用场景 关联行为场景.需要注意的是,关联行为是可拆分的,而不是"组合"关系: 事件多级触发场景: 跨系统的消息交换

Android -- AsyncTask源码解析

1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+Looper+MessageQueue机制>的时候就是想为这篇文章做铺垫的,因为AsyncTask说里面还是使用的handle,所以先就写了handle这一篇.记得15年底去美团面试的时候,面试官就问我既然存在handle为什么google还要出AsyncTask(毕竟底层还是用的handle+Exec

Android AsyncTask 源码解析

1. 官方介绍 public abstract class AsyncTask extends Object  java.lang.Object    ? android.os.AsyncTask<Params, Progress, Result> AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish resul

Android EventBus源码解析, 带你深入理解EventBus

上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么这么做不好呢? 1.概述 一般使用EventBus的组件类,类似下面这种方式: [java] view plain copy public class SampleComponent extends Fragment { @Override public vo

Android xUtils3源码解析之图片模块

初始化 x.Ext.init(this); public static void init(Application app) { TaskControllerImpl.registerInstance(); if (Ext.app == null) { Ext.app = app; } } public final class TaskControllerImpl implements TaskController { public static void registerInstance()

Android EventBus源码解析 带你深入理解EventBus

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么这么做不好呢? 1.概述 一般使用EventBus的组件类,类似下面这种方式: public cl

Android Monkey源码解析

一.使用 Monkey的使用很简单,需要注意的是各个参数的意义要搞清楚. 这篇文章并不会讲其使用,具体可以参见Google的官方文档[1],或者一篇博客[2]. 二.源码解析 1 ,参考同事和前辈的意见,阅读代码首先得理清楚主线,也即是执行流程.对应到Monkey中,也就是怎么通过在控制台中输入一串命令,就可以得到相应的测试结果的.因为主类Monkey中有main方法存在,一路跟下去,便可以理清楚其主线.下图是自己画的一个UML顺序图. 2,对于MonkeyEventSource,主要是Monk