「Android10源码分析」为什么复杂布局会产生卡顿?-- LayoutInflater详解

本文从源码角度探讨Android10中LayoutInflater的工作原理,揭示复杂布局导致卡顿的原因。主要关注LayoutInflater的创建、布局实例化过程,包括inflate、rInflate和createViewFromTag()等关键函数,以及Resources获取、XmlBlock解析和预编译优化。通过理解这些流程,为解决布局卡顿问题提供优化思路。
摘要由CSDN通过智能技术生成

系列文章索引

Android系统启动流程

  1. 源码下载及编译
  2. Android系统启动流程纵览
  3. init进程源码解析
  4. zygote进程源码解析
  5. systemServer源码解析

LayoutInflater源码详解

更新

录播回放已上传,请戳链接食用:【Android/源码/面试】LayoutInflater源码详解

前言

这篇文章会从源码的角度分析,LayoutInflater将xml文件实例化为一个view对象的流程

我们会发现,其中有两个部分是耗时的主要来源

  1. XmlResourseParser对xml的遍历
  2. 反射创建View对象导致的耗时

这两点,又跟Xml的复杂程度成正相关,Xml越复杂,则递归调用所消耗的时间就越长,就产生了我们所说的,卡顿问题

整体流程概览

LayoutInflater的创建

概览

LayoutInflater是一个抽象类,它的创建,并不是交由App层处理的,而是调用了from()的静态函数,经由系统服务LAYOUT_INFLATER_SERVICE,最终创建了一个LayoutInflater的子类对象–PhoneLayoutInflater

重要函数解析

LayoutInflater.from(cxt)

这个函数比较简单,就是根据传递过来的Context对象,调用getSystemService()来获取对应的系统服务,并赋值给LayoutInflater

public static LayoutInflater from(Context context) {
    
        LayoutInflater LayoutInflater =  //LayoutInflate是一个系统服务,最终返回的是`PhoneLahyoutInflater`
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
   
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

Context本身是一个抽象类,它真正的实例化对象,是ContextImpl, 在这个类的getSystemService()函数中,真正执行获取系统服务的类,是SystemServiceRegistry,其中又封装了一个ServiceFetcher来获取真正的系统服务,所有的系统服务,都是存储在一个map集合–SYSTEM_SERVICE_FETCHERS当中,这里其实是一个get方法,是从这个map集合中取出对应的系统服务

LayoutInflater

@Override
    public Object getSystemService(String name) {
   
        return SystemServiceRegistry.getSystemService(this, name);
    }

SystemServiceRegistry

/**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
   
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

关于对应的服务的添加,也就是调用了SYSTEM_SERVICE_FETCHERS的put函数,这个动作是交由registerService()来完成的

/**
     * Statically registers a system service with the context.
     * This method must be called during static initialization only.
     */
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
   
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

SystemServiceRegistry这个类中有一个静态代码块,是用来完成所有服务的注册的,这里我们只关心LAYOUT_INFLATER_SERVICE对应的服务是如何注册的

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

正如我们之前所说,这里最终是创建了一个PhoneLayoutInflater并返回的,到这里LayoutInflater的创建流程就分析完了

思考

为什么要交由系统服务来做,而不是直接创建一个PhoneLayoutInflater的实例对象?

LayoutInflater布局的实例化

整体流程

实例化的调用流程我们都很熟悉了,调用layoutInflaterinflater()函数,传入一个xml的resId参数就可以了

重要函数解析

inflate

这个函数就是我们把Xml布局文件实例化为一个View对象的入口了

LayoutInflater

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) + ")");
        }

        View view = tryInflatePrecompiled(resource, res, root, attachToRoot); //这段代码其实是必然返回null的,因为当前版本写死了预编译的Enable为false
        if (view != null) {
   
            return view;
        }
        XmlResourceParser parser = res.getLayout(resource); //获取XmlBlock.Parser对象
        try {
   
            return inflate(parser, root, attachToRoot); 
        } finally {
   
            parser.close(); 
        }
    }

此处又调用了inflate(parser, root, attachToRoot)这个函数,来对Xml布局进行解析

这里看到对一些熟悉的标签,比如include,merge,的处理,具体细节请看下面的源码及注释

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
   //XmlPullParser是一个接口
		//此函数是真正执行将xml解析为视图view的过程,此处的parser为根据xml布局获取到的parser对象
        synchronized (mConstructorArgs) {
   
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final inflateAttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

释然小师弟

觉得不错?不妨请我喝杯咖啡?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值