关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android TextView 预渲染详解

    Android TextView 预渲染详解

    2015-07-28 00:00:00 出处:codeceo
    分享

    Android中的Textview是整个framework中最复杂的控件之一,负责Android中显示文本的大部分工作,framwork中 的许多控件也直接或者间接的继承于TextView,例如Button,EditText等。其内部实现也相当复杂,单论代码行数来说,android- 22中TextView有足足9509行,另外,TextView中许多操作都非常繁重,例如setText操作,需要设置 SpanWatcher,或者需要重现创建一个SpannableString,还需要根据情况重新创建Text Layout,这些操作加起来之后令一次setText操作非常耗时。为了提升TextView的渲染效率,最近研究了一下预渲染的方法,接下来给大家讲 解一下原理。

    TextView渲染基本原理

    首先来介绍下TextView的基本渲染原理,总的来说,TextView中负责渲染文字的主要是这三个类:

    BoringLayout
    主要负责显示单行文本,并提供了isBoring方法来判断是否满足单行文本的条件。 DynamicLayout
    当文本为Spannable的时候,TextView就会使用它来负责文本的显示,在内部设置了SpanWatcher,当检测到span改变的时候,会进行reflow,重新计算布局。 StaticLayout
    当文本为非单行文本,且非Spannable的时候,就会使用StaticLayout,内部并不会监听span的变化,因此效率上会比 DynamicLayout高,只需一次布局的创建即可,但其实内部也能显示SpannableString,只是不能在span变化之后重新进行布局而 已。

    另外,以上三个类都继承于Layout类,在此类中统一负责文本的具体绘制,在Layout.draw方法中,会对文本一行一行的进行渲染:

    TextLine tl = TextLine.obtain();
    
    // Draw the lines, one at a time.
    // The baseline is the top of the following line minus the current line's descent.
    for (int i = firstLine; i <= lastLine; i++) {
          ....
          Directions directions = getLineDirections(i);
          if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
             // XXX: assumes there's nothing additional to be done
              canvas.drawText(buf, start, end, x, lbaseline, paint);
          } else {
              tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
              tl.draw(canvas, x, ltop, lbaseline, lbottom);
          }
    }
    TextLine.recycle(tl);

    可以看出来对于Spannble,或者包含emoji的文本的话,实际渲染操作是交给了TextLine去绘制,否则直接使用canvas.drawText,TextLine负责单行复杂文本的绘制,其中Spannable, Emoji之类的绘制逻辑都包含在里面,TextLine的绘制逻辑也并非十分高效,这里后续将会继续说明其应该如何优化。

    TextLayoutCache

    Canvas在drawText的时候,假如需要每次都计算字体的大小,边距等之类的话,就会非常耗时,导致 drawText时间会拉的很长,为了提高效率,android在4.0之后引入了TextLayoutCache,使用LRU Cache缓存了字形,边距等数据,提升了drawText的速度,在4.4中,这个cache的大小是0.5M,全局使用,并且会在Activity的configurationChanged,onResume,lowMemory,updateVisibility等时机,会调用Canvas.freeTextLayoutCache来释放这部分内存。由于这部分的cache是系统底层控制的,我们无法做具体的控制。

    TextView的预渲染优化

    从TextView的渲染原理来看,假如只是单纯的显示文本,我们根本不需要另外设置SpanWatcher来监听span的变化,因此我们可以直 接使用BoringLayout或者StaticLayout来直接显示文本内容,但是BoringLayout只能显示单行文本,因此这里最好的选择是 直接用StaticLayout

    我们选择了自定义View,并希望最终有这样的一个接口:

    public class StaticLayoutView extends View {
        private Layout layout = null;
        public void setLayout(Layout layout) {
            this.layout = layout;
            requestLayout();
        } 
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            canvas.save();
            if (layout != null) {
                layout.draw(canvas, null, null, 0);
            }
            canvas.restore();
        }
    }

    我们可以直接通过设置这个view的Layout来绘制文本,并在onDraw方法中直接使用这个Layout对象来绘制文本。在这里我们摒弃了setText方法,直接通过Layout来绘制文本,而这里的Layout对象,我们可以通过预先创建之后才设置进去(这里可以放到单独的一个线程中创建),这样对比起普通TextView的setText方法,我们减少了setText中的许多消耗,可以大幅度的提升效率。

    StaticLayout的创建非常简单,只需要给定文本,宽度等就能直接创建。另外,为了预先填充TextLayoutCache,我们也可以在创建完StaticLayout对象之后,预先在一个dummy canvas中draw出来:

    StaticLayout layout = new StaticLayout(TestSpan.getSpanString(i), textPaint, hardCodeWidth, alignment, 1.0f, 0f, true);
    layout.draw(dummyCanvas);

    性能对比

    接下来我们测试一下具体的性能,这里的testcase放到了Github上:StaticLayoutView

    testcase的内容为,在一个ListView中,显示300个Item,每个item都是一段纯文本,里面全都是包含有大量 ImageSpan的SpannableString,进行两边的对比,一边是直接使用StaticLayout,一边是使用普通的TextView,并 且这300段文本不全相同,长度不同,随机生成,在StaticLayout的testcase中,StaticLayout都是预先在另外一个线程创建 好之后才设置进去的,另外SpannableString也是预先生成好的。

    另外,在这里为了模拟真实app繁重的后台工作,另外创建了3个线程,不停在做浮点预算以尝试抢占CPU资源。

    测量性能的指标为,ListView连续向下滚动,测量其平均帧率为多少,分别测量五次,计算其平均值,最终性能测试结果如下:

    Android TextView预渲染研究

    这里测试的机器是MX3,左侧是直接使用StaticLayout的方案,右侧是系统的默认方案,Y轴是FPS,可以看出来,使用优化之后的方案,帧率提升了许多。

    References

    Improving Comment Rendering on Android 该文介绍了Instagram如何优化他们的TextView渲染的效率,这也是这里优化方法的来源,Instagram也是直接使用 StaticLayout并通过预先创建Layout的方法来减少了ListView滚动过程中的掉帧率,并且效果非常显著。该文算是给出了这里的原 理解析以及一个简单的实现。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。