小白都看得懂的布局加载流程(代码片段)

懂你的大海 懂你的大海     2022-12-19     426

关键词:

本篇文章的起点是从 ActivitysetContentView方法说起。我先放一张加载布局的时序图,布局加载涉及的类相对比较少一些。

1. Activity#setContentView()

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

这里 getWindow() 返回的是 Window 类型的,它是一个抽象类,变量名为 mWindow,之前看过我写的 Activity 启动流程的应该知道,它是在 Activityattach方法中初始化的,可以看下面的代码:

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) 
    attachBaseContext(context);

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

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

创建出实现类 PhoneWindow,其实 Window 也只有一个实现类就是 PhoneWindow. 接下来,就到了 PhoneWindowsetContentView 方法中了,继续跟进。

2. PhoneWindow#setContentView

@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) 
        installDecor();//------------------->2
     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);//------------------->2
    
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) 
        cb.onContentChanged();
    
    mContentParentExplicitlySet = true;

注释1处是对 DecorView 进行初始化,这个不是我们本篇文档的重点,我们看下注释2 是用 LayoutInflater 对我们的资源id 进行加载:

3.LayoutInflater#inflate

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) 
    return inflate(resource, root, root != null);

两个参数的会调用三个参数的方法,其中 第三个参数的话肯定就是 true了,因为我们上面穿了 mContentParent,这个值会在 installDecor 方法中进行初始化的。

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 
    final Resources res = getContext().getResources();
    if (DEBUG) 
        Log.d(TAG, "INFLATING from resource: "" + res.getResourceName(resource) + "" ("
                + Integer.toHexString(resource) + ")");
    

    final XmlResourceParser parser = res.getLayout(resource);
    try 
        return inflate(parser, root, attachToRoot);
     finally 
        parser.close();
    

XMLResourceParser 负责加载解析我们 xml ,最后交给 inflate 方法,我曾经在 解决布局加载那些奇奇怪怪的事 中对这部分代码做过详细的解释,在里面的 createViewFromTag 方法中,对 View 进行反射,看下代码:

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
        boolean ignoreThemeAttr) 
    if (name.equals("view")) 
        name = attrs.getAttributeValue(null, "class");
    

    // Apply a theme wrapper, if allowed and one is specified.
    if (!ignoreThemeAttr) 
        final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
        final int themeResId = ta.getResourceId(0, 0);
        if (themeResId != 0) 
            context = new ContextThemeWrapper(context, themeResId);
        
        ta.recycle();
    

    if (name.equals(TAG_1995)) 
        // Let's party like it's 1995!
        return new BlinkLayout(context, attrs);
    

    ...
        View view;
        if (mFactory2 != null) //------------->1
            view = mFactory2.onCreateView(parent, name, context, attrs);
         else if (mFactory != null) //------------>2
            view = mFactory.onCreateView(name, context, attrs);
         else 
            view = null;
        

        if (view == null && mPrivateFactory != null) 
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        

        if (view == null) 
            final Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = context;
            try 
                if (-1 == name.indexOf('.')) //----------->3
                    view = onCreateView(parent, name, attrs);
                 else //----------------->4
                    view = createView(name, null, attrs);
                
             finally 
                mConstructorArgs[0] = lastContext;
            
        

        return view;
    ...

注释1:判断是否设置了 mFactory2 ; 注释2:判断是否设置了 mFactory; 如果我们设置了 Factory ,就会调用相应的 onCreateView 方法,利用它我们可以做很多事情,像统计布局控件的绘制速度,全局替换某个控件等,这个我在后面文章会介绍它的使用。 注释3:判断我们在xml 中的控件是否自定义控件,如果是自定义控件的话,一般我们都是 xxx.MyView 一定会包含 .,如果含有的话就是自定义控件,否则就是进入注释4,系统控件,例如:TextView 这样,进入 createView 方法:

public final View createView(String name, String prefix, AttributeSet attrs)
        throws ClassNotFoundException, InflateException 
    Constructor<? extends View> constructor = sConstructorMap.get(name);
    if (constructor != null && !verifyClassLoader(constructor)) 
        constructor = null;
        sConstructorMap.remove(name);
    
    Class<? extends View> clazz = null;

    try 
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);

        if (constructor == null) 
            // Class not found in the cache, see if it's real, and try to add it
            clazz = mContext.getClassLoader().loadClass(
                    prefix != null ? (prefix + name) : name).asSubclass(View.class);

            if (mFilter != null && clazz != null) 
                boolean allowed = mFilter.onLoadClass(clazz);
                if (!allowed) 
                    failNotAllowed(name, prefix, attrs);
                
            
            constructor = clazz.getConstructor(mConstructorSignature);
            constructor.setAccessible(true);
            sConstructorMap.put(name, constructor);
         else 
            // If we have a filter, apply it to cached constructor
            if (mFilter != null) 
                // Have we seen this name before?
                Boolean allowedState = mFilterMap.get(name);
                if (allowedState == null) 
                    // New class -- remember whether it is allowed
                    clazz = mContext.getClassLoader().loadClass(
                            prefix != null ? (prefix + name) : name).asSubclass(View.class);

                    boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                    mFilterMap.put(name, allowed);
                    if (!allowed) 
                        failNotAllowed(name, prefix, attrs);
                    
                 else if (allowedState.equals(Boolean.FALSE)) 
                    failNotAllowed(name, prefix, attrs);
                
            
        

        Object lastContext = mConstructorArgs[0];
        if (mConstructorArgs[0] == null) 
            // Fill in the context if not already within inflation.
            mConstructorArgs[0] = mContext;
        
        Object[] args = mConstructorArgs;
        args[1] = attrs;

        final View view = constructor.newInstance(args);
        if (view instanceof ViewStub) 
            // Use the same context when inflating ViewStub later.
            final ViewStub viewStub = (ViewStub) view;
            viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
        
        mConstructorArgs[0] = lastContext;
        return view;

    ...

可以看到我们对 View 最终是通过反射创建出来的,并且对View进行了缓存.

sConstructorMap.put(name, constructor);

今日分享就到这里了,有问题评论区见~

linux楼下大爷都看得懂的用户权限,文件权限相关指令及其示范!!(代码片段)

权限用户权限susudo新增用户,设置密码文件权限解读rwx规则解读umaskchmod沾滞位chownchgrp用户权限susu用于切换用户。Linux用户分为超级用户(root)和普通用户。超级用户拥有所有权限。切换示例:surootsudevsudo表示普... 查看详情

hdu1024maxsumplusplus小白都可以看得懂的解析

这道题弄了很久,网上的很多都看不懂,所以想要写一个像我这种菜鸟都可以看得懂的解析。题意是将一个长度为n的序列,分成m段不相交叉的子段,使得他们的和最大。于是可以用dp[i][j]来表示在前j个数中,以num[j]结尾并分为i... 查看详情

小白都能看得懂的教程一本教你如何在前端实现富文本编辑器(代码片段)

小白都能看得懂的教程一本教你如何在前端实现富文本编辑器博主博客文章内容导航(实时更新)更多优质文章推荐:收藏!最详细的Python全栈开发指南看完这篇你还不会Python全栈开发你来打我!!!一本教你如何在... 查看详情

小白都能看得懂的教程一本教你如何在前端实现markdown编辑器(代码片段)

小白都能看得懂的教程一本教你如何在前端实现markdown编辑器  大家好,我是亓官劼(qíguānjié),在【亓官劼】公众号、CSDN、GitHub、B站、华为开发者论坛等平台分享一些技术博文,主要包括前端开发、pyth... 查看详情

❤️0基础小白也能看得懂的数据结构之栈❤️⭐建议收藏⭐(代码片段)

⭐欢迎来到数据结构专栏,一起学习,一起进步⭐⭐文章目录:一、栈是什么?二、集合框架中的栈三、栈方法的处使用3.1、初次使用Stack栈的方法3.2、栈的push方法3.3、栈的pop方法3.4、栈的peek方法3.5栈的empty方法3... 查看详情

人人都看得懂的正则表达式教程

编写验证规则最流行和最简单的方法就是正则表达式了,但唯一的一个问题是正则表达式的语法太隐晦了,让人蛋疼无比。很多开发者为了在项目中应用复杂的验证,经常要使用一些小抄来记住正则式的复杂语法和各种常用命令... 查看详情

小白都能看得懂的教程看完这篇还不会生成随机验证码图片,你来打我!!!(代码片段)

小白都能看得懂的教程一文教你实现生成随机图像验证码大家好,我叫亓官劼(qíguānjié),三本计算机在读,目前在积极准备21计算机考研中,同时也在学习后端开发,准备工作。不敢孤注一掷,... 查看详情

莽村李青都看得懂的vue响应式原理(代码片段)

Vue响应式原理八股文序违背老祖宗的决定将Vue响应式原理公众于世响应式数据(Observe篇)dom更新(Wacther篇)依赖收集八股文序开篇来一段大家都会背诵的八股文。某面试官:请你简要介绍一下Vue的响应式原理... 查看详情

promise之你看得懂的promise(代码片段)

本文由作者陈旭锋(任职网易考拉)授权网易云社区发布。Promise源码详解学习知识要善于思考,思考,再思考。——爱因斯坦1.回调地狱曾几何时,我们的代码是这样的,为了拿到回调的结果,不得不callbackhell,这种环环相扣的... 查看详情

promise之你看得懂的promise(代码片段)

本文由作者陈旭锋(任职网易考拉)授权网易云社区发布。Promise源码详解学习知识要善于思考,思考,再思考。——爱因斯坦1.回调地狱曾几何时,我们的代码是这样的,为了拿到回调的结果,不得不callbackhell,这种环环相扣的... 查看详情

转载极域九法——小白看得懂的退出极域电子教室教程

...从没有说这里是所有的方法,只是我们常用的,给可怜的小白们科普一下, 查看详情

入门必看-算法基础知识讲解小白都也能看得懂(代码片段)

💂个人主页:IT学习日记🤟版权:本文由【IT学习日记】原创、在CSDN首发、需要转载请联系博主💬如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦💅想寻找共同成长的小伙伴,请点击【技... 查看详情

机器学习什么是gan小孩都看得懂的gan

以下内容来自:王圣元 王的机器0 GAN是什么GAN的全称是GenerativeAdversarialNetwork,中文是生成对抗网络。一言以蔽之,GAN包含了两个神经网络,生成器(generator)和辨别器(discriminator),两者互... 查看详情

关于程序员的笑话,你都看得懂吗?

程序猿的读书历程:x语言入门—>x语言应用实践—>x语言高阶编程—>x语言的科学与艺术—>编程之美—>编程之道—>编程之禅—>颈椎病康复指南。问:程序猿最讨厌康熙的哪个儿子?答:胤禩,因为他是八阿哥(bug... 查看详情

你不得不看,巨详细王大爷表示都看得懂(代码片段)

今天是6月8号高考第二天,先祝考生们金榜题名哈,接着进入正题…C语言版八大排序算法1.插入排序2.希尔(Shell)排序3.选择排序4.堆排序5.冒泡排序6.快速排序6.1递归方法6.2.1Hoare方法6.2.2填坑法6.2.3前后指针法6.2.4快排优化6.2... 查看详情

本科生学深度学习-史上最容易懂的rnn文章,小白也能看得懂,评论继续送书(代码片段)

目录1、rnn是什么2、原理说明2.1rnn和全连接神经网略的区别2.2RNN简单图解释2.3RNN展开图解释2.4RNN的一些点3、rnn的伪代码表示4、来个小例子5、rnn存在的问题6、总结最近写了一些基础的东西,总是理解性的,没有看到实例&#... 查看详情

本科生学深度学习-史上最容易懂的rnn文章,小白也能看得懂,评论继续送书(代码片段)

目录1、rnn是什么2、原理说明2.1rnn和全连接神经网略的区别2.2RNN简单图解释2.3RNN展开图解释2.4RNN的一些点3、rnn的伪代码表示4、来个小例子5、rnn存在的问题6、总结最近写了一些基础的东西,总是理解性的,没有看到实例&#... 查看详情

java小白也能轻松写一个大神都看不懂的程序,绝对让人佩服(代码片段)

前言你想在别人面前秀一把你的“神功”吗?跟着二当家的轻松学到“武功根基”,秀他们一脸的迷茫。本文由二当家的白帽子https://le-yi.blog.csdn.net/博客原创,转载请注明来源,谢谢~文章目录前言开始秀尾声开... 查看详情