LayoutInflater 流程解析

本文详细剖析了Android中LayoutInflater的流程,从Activity的setContentView开始,深入到PhoneWindow、DecorView和LayoutInflater类的源码分析,揭示了系统如何加载布局和创建View的过程。文章覆盖了关键方法如installDecor、generateDecor、inflate等,并介绍了createViewFromTag和createView的创建过程,以及findViewById的查找逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

LayoutInflater 流程解析

在上一篇文章《Android Style和自定义属性》中,大家对Style和Theme的使用有了比较清晰的认识,知道了在项目中如何更合理的规划Style。有同学就提问了,在xml文件中定义的属性,是如何加载并传递给View呢?View又是如何创建的呢?

带着这两个问题,我们来阅读Android LayoutInflater相关的源码,阅读这块的源码,主要涉及四个类:

  • frameworks/base/core/java/android/app/Activity.java
  • frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
  • frameworks/base/core/java/com/android/internal/policy/DecorView.java
  • frameworks/base/core/java/com/android/view/LayoutInflater.java

在Activity的onCreate()方法中,我们会对当前Activity设置布局,我们以这儿为入口来进行研究。

public class MainActivity extends Activity {
   
   
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
   
        super.onCreate(savedInstanceState);
        ...
        setContentView(R.layout.activity_main);
        ...
    }
    ...
}

1 frameworks/base/core/java/android/app/Activity.java

1.1 Activity#setContentView(int layoutResID)

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

1.2 Activity#getWindow()

getWindow()会返回一个Window实例化对象

public Window getWindow() {
   
   
    return mWindow;
}

而mWindow的初始化是在attach中

1.3 Activity#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,
        Window window, ActivityConfigCallback activityConfigCallback) {
   
   
    ...
    // 初始化一个PhoneWindow对象
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ...
    // 给window对象设置回调,Window的实例化对象可以回调Activity的回调方法
    mWindow.setCallback(this);
    ...
}

2 frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

private DecorView mDecor;

mDecor是继承自FrameLayout的ViewGroup,它是整个Window的最顶层view,包含状态栏,导航栏,内容显示栏三块区域。具体层级关系见下图:
层级关系图

2.1 PhoneWindow#PhoneWindow(Context context)

public PhoneWindow(Context context) {
   
   
    super(context);
    mLayoutInflater = LayoutInflater.from(context);
}

这里初始化了mLayoutInflater,最终执行(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)获取到LayoutInflater的实现类,具体对象为PhoneLayoutInflater。

2.2 PhoneWindow#setContentView(int layoutResID)

// mContentParent是一个放置Window内容的ViewGroup实例,它是mDecor本身,或mDecor的内容所在的子节点。
ViewGroup mContentParent;

public void setContentView(int layoutResID) {
   
   
    // 判断mContentParent是否为空,为空执行installDecor(),第一次进来为空;
    // 否则判断是否设置Activity的场景切换动画,默认为flase,则会将mContentParent中的子View全部清除
    if (mContentParent == null) {
   
   
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
   
   
        mContentParent.removeAllViews();
    }

    // 如果设置了Activity的场景切换动画,则会根据layoutResID获取到场景并赋值给mTransitionManager
    // 否则将根据layoutResID加载布局并设置给mContentParent
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
   
   
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
        transitionTo(newScene);
    } else {
   
   
        // *************** 核心方法,重点 ***************
        // 该方法的作用是将ID为layoutResID的布局作为子布局加入mContentParent中
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    // 回调Callback#onContentChanged()通知Activity
    // onContentChanged()在Activity中为空方法,我们可以在自己的Activity中复写这个方法,实现自己的逻辑。在Activity布局文件发生改动,即调用setContentView()或者addContentView()之后会调用onContentChanged()。
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
   
   
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

这个函数中有两个重要方法:installDecor()和mLayoutInflater.inflate(layoutResID, mContentParent)。
这两个方法分别加载的是Android源码中的系统根布局和用户自定义的R.layout.activity_main布局。我们主要看下Android是如何加载系统根布局的,看懂了之后,加载用户自定义布局也就融会贯通了。

2.3 PhoneWindow#installDecor()

private void installDecor() {
   
   
    // 如果mDecor为空,则实例化;否则将当前window设置给mDecor
    if (mDecor == null) {
   
   
        mDecor = generateDecor(-1);
        ...
    } else {
   
   
        mDecor.setWindow(this);
    }
    // 如果mContentParent为空,则根据mDecor获取到mContentParent
    if (mContentParent == null) {
   
   
        mContentParent = generateLayout(mDecor);
        ...
    }
}

2.4 PhoneWindow#generateDecor(int featureId)

protected DecorView generateDecor(int featureId) {
   
   
    ...
    // new一个DecorView对象
    return new DecorView(context, featureId, this, getAttributes());
}

注意:
此处有个getAttributes()方法,该方法为父类Window中的方法,返回WindowManager.LayoutParams类。可见,该类返回的是当前Window的一些属性,这些属性的赋值通过setAttributes()方法。

2.5 PhoneWindow#generateLayout(DecorView decor)

protected ViewGroup generateLayout(DecorView decor) {
   
   
    ...
    int layoutResource;
    int features = getLocalFeatures();
    ...
    // 根据features的值对layoutResource进行初始化
    ...
    mDecor.startChanging();
    // 根据layoutResource加载布局资源
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    // 生成contentParent,其中ID_ANDROID_CONTENT=com.android.internal.R.id.content
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    mDecor.finishChanging();
    // 返回contentParent
    return contentParent;
}

这里layoutResource会根据features的值来进行初始化,以加载不同的布局,但是无论是什么布局,其中必然包含一个ID为content的控件,一般为FrameLayout。
例如layoutResource初始化为R.layout.screen_title_icons:

<LinearLayout xmlns:android
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值