得物布局构建耗时优化方案实践

本文分享得物在页面布局构建耗时优化方案的探索。先介绍掌阅X2C、AsyncLayoutInflater、ViewCompiler等现有方案及优缺点,接着阐述自研X2C框架实践,包括View构建流程、插件选型、预加载、线程调优等,最后展示线上性能收益,强调数据驱动优化。

一、背景

当谈到移动应用程序的体验时,页面启动速度是其中至关重要的一点,更快的页面展示速度确保应用程序可以迅速加载并响应用户的操作, 从而提高用户使用 App 时的满意度。在页面启动的整个流程中,随着 UI 复杂度的上升,布局的 Inflate 耗时占据了相当一部分关键的比例,本文分享得物自身在页面布局构建耗时优化方案上的探索历程。

二、现有方案

在布局构建耗时优化上,开源社区上有一些现成的方案可供参考,我们首先看下目前一些已知的技术方案。

掌阅X2C

掌阅的 X2C 方案开源于 2018 年,其通过 APT 在编译期间对目标 XML 文件进行解析,并翻译成 XML View 树结构对应的 Java 文件。比如以下的布局 XML 文件。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingLeft="10dp">

  <include
      android:id="@+id/head"
      layout="@layout/head"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true" />

  <ImageView
      android:id="@+id/ccc"
      style="@style/bb"
      android:layout_below="@id/head" />
</RelativeLayout>

转换成 Java 文件:

public class X2C_2131296281_Activity_Main implements IViewCreator {
  @Override
  public View createView(Context ctx, int layoutId) {
        Resources res = ctx.getResources();

        RelativeLayout relativeLayout0 = new RelativeLayout(ctx);
        relativeLayout0.setPadding((int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,res.getDisplayMetrics())),0,0,0);

        View view1 =(View) new X2C_2131296283_Head().createView(ctx,0);
        RelativeLayout.LayoutParams layoutParam1 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        view1.setLayoutParams(layoutParam1);
        relativeLayout0.addView(view1);
        view1.setId(R.id.head);
        layoutParam1.addRule(RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE);

        ImageView imageView2 = new ImageView(ctx);
        RelativeLayout.LayoutParams layoutParam2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,(int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,res.getDisplayMetrics())));
        imageView2.setLayoutParams(layoutParam2);
        relativeLayout0.addView(imageView2);
        imageView2.setId(R.id.ccc);
        layoutParam2.addRule(RelativeLayout.BELOW,R.id.head);

        return relativeLayout0;
  }
}

344.png

优点:

  • 性能高,没有了加载 XML 的 IO 和递归解析过程。
  • 避免了类反射构建的耗时。
  • 基于 APT 直接生成 Java 文件。

缺点:

  • View 兼容性差,适配成本高,自定义 View 需要配置属性对应的方法。
  • 功能不完整,不支持 Merge 标签,无法查询系统 style,所以只支持应用内 style。
  • 由于 APT 本身的特性,在 XML 发生变化时,对应注解处理器生成的 Java 构建文件不会同步发生变, 对于不熟悉的同学来说容易踩坑。

AsyncLayoutInflater

AsyncLayoutInflater 是由 Android Google 官方提供的异步 Inflate API,其主要思路是将 Inflate 操作放在异步线程并行操作,从而让主线程可以继续执行一些其他的初始化操作,通过异步回调在相应的 Layout View 创建完成后,再设置到页面上。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    new AsyncLayoutInflater(this).inflate(
            R.layout.async_layout,
            null,
            new AsyncLayoutInflater.OnInflateFinishedListener() {
                @Override
                public void onInflateFinished(View view, int resid, ViewGroup parent) {
                    setContentView(view);
                }
            }
    );
}

优点:

  • 将 UI 加载过程迁移到了子线程,保证了 UI 线程的高响应。
  • 不存在 View 兼容性问题。

缺点:

  • 有一定改造成本,在原有的页面直接引入 AsyncLayoutInflater 进行改造时,由于从同步调用改成异步回调调用导致的逻辑结构变化容易引入 NPE 之类的风险。

  • 内部依然存在部分 View 的反射需要创建的开销。

ViewCompiler

Google 加入了一个 ViewCompiler,从原理来看是系统在安装 APK 的时候自动对布局文件做的编译优化,ViewCompiler 会将可优化的 XML 布局转化为代码构建的代码,并编译成 Dex 文件。

333.png

之后在程序运行时,首次使用 Infalter 类时,就会提前加载该 Dex 文件。

888.png

之后在调用 Infalte 函数 Inflate相应布局资源时,会尝试调用优化后的 pacakgeme.CompileView 类的 Infalte 函数,直接生成对应的 View。

099.png

088.png

ViewCompiler 编译 Layout 的原理其实和现有的 XML To Code 方案是类似的,都是解析 Layout XML 文件,再根据 XML 节点信息生产组装 Vi

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值