美文网首页Android 知识爱上Androidandroid
【掌阅出品】android 提升布局加载速度200%(X2C)

【掌阅出品】android 提升布局加载速度200%(X2C)

作者: 风无羽 | 来源:发表于2018-08-16 20:04 被阅读2250次

    背景

     一般大家在写页面时都是通过xml写布局,通过setContentView、或LayoutInflater.from(context).inflate方法将xml布局加载到内存中。

    优点

    • 可维护性好
    • 支持即时预览
    • 代码结构清晰

    缺点

    • 读取xml很耗时
    • 递归解析xml较耗时
    • 反射生成对象的耗时是new的3倍以上

        我们团队在这个问题上也探索过很多解决方案,一度走到了另一个极端,完全废弃xml,所有控件通过java来new,甚至直接在canvas里绘制,这样虽然性能确实提升了,但是代码已经没有了一丁点可读性,可维护性。
        我们后来反思代码到底是给机器看的,还是给人看的??也许X2C已经给了我们一个答案

    X2C

        为了即保留xml的优点,又解决它带来的性能问题,我们开发了X2C方案。即在编译生成APK期间,将需要翻译的layout翻译生成对应的java文件,这样对于开发人员来说写布局还是写原来的xml,但对于程序来说,运行时加载的是对应的java文件。
        我们采用APT(Annotation Processor Tool)+ JavaPoet技术来完成编译期间【注解】->【解注解】->【翻译xml】->【生成java】整个流程的操作。

    性能

    在开发集成完之后我们做了简单的测试,性能对比如下

    加载方式 次数 平均加载时间
    XML 100 30
    X2C 100 11

    集成使用

    1.导入依赖

    annotationProcessor project(':apt')
    implementation project(':lib')
    

    2.添加注解

    @Xml(layouts = {R.layout.activity_main})
    

    3.通过X2C加载布局

    X2C.setContentView(this, R.layout.activity_main);
    
    X2C.inflate(this,R.layout.activity_main,null);
    

    过程文件

    原始的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文件

     
    /**
     * WARN!!! dont edit this file
     * translate from {@link  com.zhangyue.we.x2c.demo.R.layout.activity_main}
     * autho chengwei 
     * email chengwei@zhangyue.com
     */
    public class X2C_A7f_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_A7f_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;
      }
    }
    

    生成的映射文件

    /**
     * WARN!!! dont edit this file
     *
     * autho chengwei 
     * email chengwei@zhangyue.com
     */
    public class X2C_A7f implements IViewCreator {
      @Override
      public View createView(Context context, int layoutId) {
            View view;
            switch(layoutId){
                case 2131296283:
                    view = new X2C_A7f_Activity_Main().createView(context,2131296283);
                    break;
                case 2131296284:
                    view = new X2C_A7f_Head().createView(context,2131296284);
                    break;
                default:
                    view = null;
                    break;
            }
            return view;
      }
    }
    
    

    不支持的

    • merge标签 ,在编译期间无法确定xml的parent,所以无法支持
    • 系统style,在编译期间只能查到应用的style列表,无法查询系统style,所以只支持应用内style

    支持的

    • 各种系统控件、自定义控件
    • include标签
    • 应用style
    • android开放属性
    属性名称
    android:textSize
    android:textColor
    android:text
    android:background
    android:layout_marginLeft
    android:layout_marginTop
    android:layout_marginRight
    android:layout_marginBottom
    android:paddingLeft
    android:paddingTop
    android:paddingRight
    android:paddingBottom
    android:padding
    android:gravity
    android:orientation
    android:id
    android:layout_centerInParent
    android:layout_centerVertical
    android:layout_centerHorizontal
    android:layout_alignParentLeft
    android:layout_alignParentTop
    android:layout_alignParentRight
    android:layout_alignParentBottom
    android:layout_above
    android:layout_below
    android:layout_toLeftOf
    android:layout_toRightOf
    android:layout_alignLeft
    android:layout_alignTop
    android:layout_alignRight
    android:layout_alignBottom
    android:scaleType
    android:src
    android:visibility
    android:clipToPadding
    android:ellipsize
    android:lineSpacingExtra
    android:maxLines
    ...

    源码

    https://github.com/iReaderAndroid/X2C

    加入我们

    QQ群:558449447,添加请注明来自X2C,Android技术交流分享

    欢迎各位使用,该项目会持续维护。

    另:欢迎加入掌阅大家庭,一起研究Android新技术。简历请发送huangjian@zhangyue.com,注明应聘方向。

    相关文章

      网友评论

      • ramblejoy:感觉很犀利呀~~~
        风无羽:@ramblejoy 嗯,追求卓越是我们一贯宗旨
      • 姜康:思路非常好
        风无羽:@姜康 谢谢😜
      • csfchh:xml加载只有初次加载界面才会有楼主所说的性能劣势吧,比较好奇是什么产品对性能有这么极致的要求?不过楼主的解决思路还是很赞的,说不定哪天谷歌也用这种方式优化了呢,虽然可能性很低(毕竟手机性能过剩的时代)
        风无羽:@西城鹿杖客 是不是很痛苦😜,X2C帮你一键生成java代码
        西城鹿杖客:老哥, 银行类的app好多都是要求不用xml写布局, 都得用java代码new出布局
        风无羽:比如列表页,包含多样式item,复用率低,如果快速滑动过程中加载布局耗时久的话就能明显感觉滑动,其实市面上很多主流app在这块做的都有优化空间
      • 我的橘子分你一瓣:个人有几个想法:
        编译期间根据xml预先生成布局java 文件 ,相应的增加了编译构建时间;
        运行期间,摒弃了原始的xml布局加载、DOM数遍历、反射初始化,而是直接加载编译生成的布局java文件。刚刚在评论去看到说对FPS 有一定的优化,我个人觉得这有点牵强,我理解它仅减少了setContentView or inflate 以及实例化控件的时间 。FPS更多关注的是 视图Overdraw 、invalidate、布局嵌套等这些方面吧;
        另外 对style 、merge 、ViewStub FlexboxLayout(google) 等等其他属性以及控件 支持不是很友好,这一点就比较尴尬了。
        无论如何,还是要给作者来一个 ”右手螺旋点赞“
        风无羽:@二段媒介 嗯,编译增加的时间我们统计过,一个布局翻译耗时15ms左右,100个也大概1.5秒,而且我们只翻译注解注册的,Fps不光是measure,layout,draw等,其实生成view也会影响,比如滑动列表如果生成item耗时多的话,卡顿就明显了
      • 胡睡:你好,我把github上面的demo跑了一下,我是在调用setContentView(R.layout.activity);方法前后获取时间再相减,跟原生的写法时间几乎没有差别,请问你们团队是怎么测试的呢?或者说我这边测试方法有什么问题吗?
        风无羽:@胡睡 我们是在我们生产环境里测试的,这个和布局的复杂和设备的IO读取性能有关系,demo里的布局太简单体现不出X2C优势
      • fightfa:和anko layout DSL对比如何?
        风无羽:@fightfa 和anko 性能一样,都是通过代码加载view
      • 最爱那个女孩:不错的思路
        风无羽:@最爱那个女孩 是的,对我们来说解决了痛点问题,因为达到16fps是我们的基本要求
      • 6f748fe54ad4:违心的给你点个赞😏😏😏
        风无羽:@十纬空间 😂不诚实
      • 星际旅行android:git地址是啥
        星际旅行android:@风无羽 现在还导入不了依赖
        星际旅行android:@风无羽 这个是你从0开始写的还是基于啥
        风无羽:@星际旅行android 现在还没放上去,预计明后天,到时我会告诉你
      • AWeiLoveAndroid:代码要注意优雅性和规范性😂
        风无羽:@AWeiLoveAndroid 是的,我们即希望保留代码的优雅,要追求卓越的性能,所以才开发X2C
      • 咖啡老师:那个极端可读性真是差,除非是自己本人写的,交给后面维护的人看的心塞很久。:fearful:
      • 26e6ae3b48e1:真好!
      • 程卫_9b55:不断追求卓越的性能

      本文标题:【掌阅出品】android 提升布局加载速度200%(X2C)

      本文链接:https://www.haomeiwen.com/subject/sjoobftx.html