Android View之布局加载流程

1.引言

最近准备重新学习下Android,加深理解,快速形成自己的知识结构体系。最先学习的就算View部分,从自定义View到Activty层次结构,到layout加载过程。等等都会看一遍,在此记录下Layout的加载过程

2.正题

2.1 Activity的流程加载

Activity类中setContentView 追踪(善于用bookMark)

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

追踪代码,PhoneWindow的setContentView方法:

image.png

mLayoutInflater.inflate(layoutResId,mContentParent)

由代码片段可以看出来:layoutResId的父布局就是mContentParent。那么mContentParent究竟是什么呢?

我们知道DecorView 包括TitleView +ContentView.

image.png

追踪代码:installDescor():

image.png

generateLayout()方法:

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    // 获得窗体的 style 样式
    TypedArray a = getWindowStyle();

    // 省略大量无关代码

    // Inflate the window decor.
    int layoutResource;
    int features = getLocalFeatures();

    //填充带有 style 和 feature 属性的 layoutResource (是一个layout id)

    View in = mLayoutInflater.inflate(layoutResource, null);

    // 插入的顶层布局 DecorView 中

    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in;

    // 找到我们XML文件的父布局 contentParent 

    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn‘t find content container view");
    }
    // 省略无关代码
    mDecor.finishChanging();
    // 返回 contentParent 并赋值给成员变量 mContentParent
    return contentParent;
}

给DecorView添加一个名字为mContentRoot的view。并且mContentRoot里面包含了一个id为ID_ANDROID_CONTENT的 mContentParent.

得到了mContentParent之后,在根据mLayoutInflater.inflate(layoutResId,mContentParent) 就将Layout布局加载进了DecorView,然后这个视图才能被我们看到。

验证:

image.png

说明了mContentParent是FramLayout

3.AppCompatActivity 加载View得过程

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

getDelegate():

    @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

跟踪create()方法,

  public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

activity.getWindow() 由上面可知,得到的肯定是PhoneWindow。问题来了,activity这个时候真的能得到一个非null的PhoneWindow吗?。发现在activity调用attach方法的时候就会初始化PhoneWindow:

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

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        ......
}

Activity.attach是在performLaunchActivity中本调用,也就是在ActivityThread中被调用。所以在Activity中任何时候getWindow(),都能得到PhoneWindow。

继续跟踪代码:
create方法调用的是:

 private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return new AppCompatDelegateImplV9(context, window, callback);
        }
    }

setContentView 是在AppCompatDelegateImplV9类中被重写,也就是说最终是调用AppCompatDelegateImplV9.setContentView();

  @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
  ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);

很容易让我们想到mSubDecor 是不是等同于上文中的mContentRoot 呢?

看看ensureSubDecor怎么写的:

 if (mOverlayActionMode) {
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }

subDecor 是系统提供的ViewGroup 里面有一个android.R.id.content布局,和mContentRoot 作用一模一样。

基本上分析到此结束,也是比较简单。

原文地址:https://www.cnblogs.com/xgjblog/p/9441820.html

时间: 2024-10-08 12:58:47

Android View之布局加载流程的相关文章

android源码解析(十七)-->Activity布局加载流程

好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与android中另外一个很重要的组件Service最大的不同,但是这个展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加载到内存并被Activity管理的?android中的View是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的? 要想回答这些问题,我们就需要对android的界面加载与绘制流程有所了解,这里我们先来学习一下Act

Android 开发中 布局加载的原理

Android 加载的优先级 是怎么样的? 我现在不了解 他是如何加载的额> 我现在 遇到 的问题是 我的app 主要是面向 720 和 480 的手机 但是有一些 320 的手机 安装的时候直接崩溃 你的意思 是 android 内部加载的布局文件的话 Android 加载内部机制: 第一: 加载 指定布局的的 layout 例如 : Layout-hdpi 第二: 如果没有 layout-720 就去加载 layout! 这样就解决了 APP 中的error: java.lang.Unsup

Android View的加载流程

什么是Activity? Activity是 用户操作的可视化界面:它为用户提供了一个放置视图和交互操作的窗口.采用setContentView的方法提供.因此,可以理解Activity.Window.View三者关系为.Activity提供Window ,View被添加到Window中. 以刷墙举例: Activity可以理解为房间,Window就是房间内的墙面, 我们在墙面上可以刷各种不同的图案,这些图案就是View. Activity View的加载流程 1.Activity在被创建之初,

Android之SystemUI加载流程和NavigationBar的分析

Android之SystemUI加载流程和NavigationBar的分析 本篇只分析SystemUI的加载过程和SystemUI的其中的一个模块StatusBar的小模块NavigationBar,以Android6.0代码进行分析 AndroidManifest.xml <application android:name=".SystemUIApplication" android:persistent="true" android:allowClearU

Android 8.1 SystemUI虚拟导航键加载流程解析

需求 基于MTK 8.1平台定制导航栏部分,在左边增加音量减,右边增加音量加 思路 需求开始做之前,一定要研读SystemUI Navigation模块的代码流程!!!不要直接去网上copy别人改的需求代码,盲改的话很容易出现问题,然而无从解决.网上有老平台(8.0-)的讲解System UI的导航栏模块的博客,自行搜索.8.0对System UI还是做了不少细节上的改动,代码改动体现上也比较多,但是总体基本流程并没变. 源码阅读可以沿着一条线索去跟代码,不要过分在乎代码细节!例如我客制化这个需

Android 自定义View修炼-自定义加载进度动画LoadingImageView

一.概述 本自定义View,是加载进度动画的自定义View,继承于ImageView来实现,主要实现蒙层加载进度的加载进度效果. 支持水平左右加载和垂直上下加载四个方向,同时也支持自定义蒙层进度颜色. 直接看下面的效果图吧. 二.效果图 废话不说,先来看看效果图吧~~ 三.实现原理方案 1.自定义View-XCLoadingImageView,继承ImageVIew来实现,这样就不用自己再处理drawable和测量的工作内容. 2.根据蒙层颜色创建一个蒙层bitmap,然后根据这个bitmap来

webkit 子资源加载流程

一个网页由主文档和子资源组成.主文档描述网页的框架,布局.子资源是组成网页的子元素,包括图片.CSS.JS等.为了显示网页,先要把资源加载到内存.加载就是指把需要的资源加载到内存这一过程.Webkit用到很多缓存机制,加载可能是从网络加载,也可能是从本地缓存加载.Webkit的加载分为两条线,一条是主文档的加载,一条是子资源的加载. 首先需要解析主文档才知道用到哪些子资源.但并不一定要等到解析完主文档才加载子资源,也可能是边解析边加载子资源,即受到部分主文档就开始解析,解析到某个子资源就开始加载

Android中的分页加载

//----------------------MainActivity中--------------------------------------------------- package com.example.fenye; import java.util.ArrayList;import java.util.List; import android.os.Bundle;import android.app.Activity;import android.view.Menu;import

ListView实现下拉刷新-2-将顶部布局加载到ListView中

上一篇实现了Adapter类的创建,和getView函数的分析: 这一篇主要讲第二部分,即将顶部布局加载到ListView中:重点是ReFlashListView的实现上,这一篇中我会谈一谈在阅读源代码的过程中所遇到的困难和采取的方法: 首先看ReFlashListView类: public class ReFlashListView extends ListView implements OnScrollListener 表明ReFlashListView是继承自ListView的,并且 实现