Android布局填充器--深入LayoutInflater一探究竟

这篇具有很好参考价值的文章主要介绍了Android布局填充器--深入LayoutInflater一探究竟。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Android源码–深入LayoutInflater一探究竟(基于android-12.0.0_r34分析)

前文:

单例设计模式在Android开发实际应用场景解析–activity的管理

https://blog.csdn.net/weixin_46039528/article/details/132287718?spm=1001.2014.3001.5501

Android源码设计模式–单例模式分析,系统服务开机注册单例模式源码解析

https://blog.csdn.net/weixin_46039528/article/details/132309733?spm=1001.2014.3001.5501

LayoutInflater 是一个抽象类,找到它具体的实现类

@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {......}

系统开机注册

registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
}});

跟进PhoneLayoutInflater,这里看到PhoneLayoutInflater是LayoutInflater的一个实现类。

/**
 * @hide
 */
public class PhoneLayoutInflater extends LayoutInflater {
    //TextView 的完整路径是 android.widget.TextView,一些view的前缀字符串
    private static final String[] sClassPrefixList = {
        "android.widget.",
        "android.webkit.",
        "android.app."
    };
......
    /** Override onCreateView to instantiate names that correspond to the
        widgets known to the Widget factory. If we don't find a match,
        call through to our super class.
    */
    @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
    	//在View名字的前面添加前缀来构造 View 的完整路径,android.widget.TextView
        for (String prefix : sClassPrefixList) {
            try {
                View view = createView(name, prefix, attrs);
                if (view != null) {
                    return view;
                }
            } catch (ClassNotFoundException e) {
                // In this case we want to let the base class take a crack
                // at it.
            }
        }
......
}

核心就是onCreateView通过传进来的名字,name,prefix前缀构造出对应的View对象,比如TextView。

接下来我们康康AppCompatActivity的setContentView,它的核心就是通过LayoutInflater来加载我们的布局。AppCompatActivity 的 setContentView 方法和 Activity的 setContentView 方法是有区别的。因为我们都是继承AppCompatActivity开发较多,此处只对AppCompatActivity分析。

@Override
public void setContentView(@LayoutRes int layoutResID) {
    initViewTreeOwners();
    //getDelegate返回的是一个对象。
    getDelegate().setContentView(layoutResID);
}

跟进getDelegate,看到返回的是AppCompatDelegate,这是一个抽象类。

/**
 * @return The {@link AppCompatDelegate} being used by this Activity.
 */
@NonNull
public AppCompatDelegate getDelegate() {
    if (mDelegate == null) {
        mDelegate = AppCompatDelegate.create(this, this);
    }
    return mDelegate;
}

我们来看 AppCompatDelegate 的 create(Activity activity, AppCompatCallback callback) 方法,

    /**
     * Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code dialog}.
     *
     * @param callback An optional callback for AppCompat specific events
     */
    @NonNull
    public static AppCompatDelegate create(@NonNull Dialog dialog,
            @Nullable AppCompatCallback callback) {
        //AppCompatDelegateImpl是它的一个实现类,就是它调用了setContentView
        return new AppCompatDelegateImpl(dialog, callback);
    }

跟进到AppCompatDelegateImpl中的,有三个重载方法

  ......
    @Override
    public void setContentView(int resId) {
        //初始化DecorView
        ensureSubDecor();
        //设置setContentView 要的父布局
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        //加载之前移除所有的布局
        contentParent.removeAllViews();
        //完成布局的加载
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }
......

我们看到会加载一个系统定义好的布局contentParent,通过 LayoutInflater 的 inflate 函数将指定布局的视图添加到mContentParent 中。

 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
      ......
    	// 获取xm1资源解析器
        XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
// 参数1为xmL 解析器,参数2为要解析布局的父视图,参数3为是否将要解析的视图添加到父视图中
 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            //存储父视图
            View result = root;

           ......
			   //解析merge 标签
                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    //不是merge 标签那么直接解析布局中的视图
                    //这里就是通过 xml的 tag 来解析 layout 根视图
                    //name 就是要解析的视图的类名,如 RelativeLayout
                    //createViewFromTag会将该元素的parent 及名字传递过来
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        //生成布局参数
                        params = root.generateLayoutParams(attrs);
                        //如果attachToRoot 为 false,给 temp 设置布局参数
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }
                    ......
                    // Inflate all children under temp against its context.
                    //解析 temp 下的所有子view
                    rInflateChildren(parser, temp, attrs, true);
             ......
                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    //如果Root 不为空,attachToRoot 为true,将 temp 添加到父视图中
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    //如果root 为空或者attachToRoot 为 false,结果就是 temp
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
			......
            return result;
        }
    }

大致流程:解xml中的根标签,最外层的xml布局元素,如果根标签是 merge,那么调用rInflate进行解析,rnflate会将 merge 标签下的所有子View直接添加到根标签中,如果标签是普通元素,调用createViewFromTag 对该元素进行解析,调用rInflate解析 temp 根元素下的所有子 View,并且将这些子 View 都添加到temp下。

解析单个元素的createViewFromTag

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
		......
        try {
            //跟进这个方法
            View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    //安卓系统自带View控件的创建解析
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                     //自定义控件创建解析
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            ......
    }

跟进tryCreateView

@UnsupportedAppUsage(trackingBug = 122360734)
    @Nullable
    public final View tryCreateView(@Nullable View parent, @NonNull String name,
        @NonNull Context context,
        @NonNull AttributeSet attrs) {
 ......

        View view;
        //通过onCreateView创建View
        if (mFactory2 != null) {
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }
        ......
        return view;
    }

最终我们的view都是通过createView方法创建的。

//通过反射机制构造 view对象
@Nullable
public final View createView(@NonNull Context viewContext, @NonNull String name,
        @Nullable String prefix, @Nullable 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
            //如果 prefix不为空,那么构造完整的 View 路径加载该类
            clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                    mContext.getClassLoader()).asSubclass(View.class);
            ......
             //从class对象中获取构造函数
            constructor = clazz.getConstructor(mConstructorSignature);
            constructor.setAccessible(true);
            //将构造函数存入缓存中
            sConstructorMap.put(name, constructor);
        } else {
            ......
        }
		......
        try {
            //通过反射构造View
            final View view = constructor.newInstance(args);
  		......
}

这只是单个View的xml解析,要把子View全部解析完,我们看rInflate。文章来源地址https://www.toymoban.com/news/detail-674547.html

void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
	   //深度优先遍历算法
        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
			......
            } else {
                //元素名进行解析
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            //递归调用进行解析
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }
......
    }




到了这里,关于Android布局填充器--深入LayoutInflater一探究竟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • “深入探究JVM内部机制:如何实现Java程序的运行环境?“

    标题:深入探究JVM内部机制:如何实现Java程序的运行环境? 摘要:本文将深入探究Java虚拟机(JVM)的内部机制,重点讨论JVM如何实现Java程序的运行环境。我们将从JVM的结构、类加载、内存管理、垃圾回收等方面展开讲解,并通过示例代码具体展示JVM内部机制的运作过程。

    2024年02月11日
    浏览(38)
  • “深入探究JVM内部结构与工作原理:解析Java虚拟机“

    标题:深入探究JVM内部结构与工作原理 摘要:本文将深入探究Java虚拟机(JVM)的内部结构与工作原理。我们将介绍JVM的基本组成部分,包括类加载器、运行时数据区和执行引擎。同时,我们将通过一个示例代码来说明JVM内部结构与工作原理的具体应用。 介绍: Java虚拟机(

    2024年02月12日
    浏览(36)
  • “深入探究JVM内部机制:理解Java虚拟机的工作原理“

    标题:深入探究JVM内部机制:理解Java虚拟机的工作原理 摘要:本文将深入分析Java虚拟机(JVM)的工作原理,包括类加载、内存管理、垃圾回收和即时编译等方面。通过详细解释这些概念,并给出示例代码,帮助读者更好地理解JVM内部的工作机制。 正文: 一、类加载 类加载

    2024年02月12日
    浏览(50)
  • 【超详细】深入探究Java中的线程安全,让你的程序更加可靠~

    我们将从以下四个问题入手,对Java的多线程问题抽丝剥茧。 什么是线程安全? 如何实现线程安全? 不同的线程安全实现方法有什么区别? 如何实现HashMap线程安全? 1. 什么是线程安全? 线程安全指的是多个线程并发访问共享资源时,不会出现数据不一致或其他意外情况的

    2023年04月24日
    浏览(36)
  • Java中的单点登录原理与实现方案探究:深入了解安全与便捷的用户认证解决方案

    目录 1、什么是单点登录 2、单点登录的优势和应用场景 3、单点登录的原理和实现方式 3.1 传统的Cookie和Session实现方式 3.2 基于Token的实现方式 3.3 基于OAuth2的实现方式 4、单点登录的技术要点和关键问题 4.1 安全性考虑 4.2 用户体验优化 4.3 高可用性设计 5、Java中的单点登录实

    2024年01月23日
    浏览(59)
  • 深入理解Java虚拟机jvm-对象的内存布局

    在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例 数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机对象的对象头部分包括两类信息。第一类是用于存储对象自身的运行时数据,如哈 希码(HashCode)、GC分代年龄、锁状态标志、

    2024年02月09日
    浏览(56)
  • Java设计模式:深入解析与应用示例

    设计模式是一种在特定上下文中反复出现的可重用解决方案,用于处理软件设计中常见的问题。掌握设计模式不仅可以帮助我们编写出更优雅、更易于理解和维护的代码,而且也是Java面试中的常考知识点。在本文中,我们将探讨几种常见的设计模式,包括它们的定义、使用场

    2024年02月09日
    浏览(38)
  • 深入探究for...range语句

    在Go语言中,我们经常需要对数据集合进行遍历操作。对于数组来说,使用for语句可以很方便地完成遍历。然而,当我们面对其他数据类型,如map、string 和 channel 时,使用普通的for循环无法直接完成遍历。为了更加便捷地遍历这些数据类型,Go语言引入了for...range语句。本文将

    2024年02月08日
    浏览(49)
  • 深入浅出:探究过完备字典矩阵

    在数学和信号处理的世界里,我们总是在寻找表达数据的最佳方式。在这篇博文中,我们将探讨一种特殊的矩阵——过完备字典矩阵,这是线性代数和信号处理中一个非常有趣且实用的概念。 首先,我们先来理解一下字典矩阵的概念。在数学上,字典矩阵基本上就是一组向量

    2024年03月17日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包