文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

抖音 Android 端图片优化实践

2024-11-29 21:36

关注

背景介绍

抖音为什么要持续优化图片能力

图片能力作为抖音最基础的能力之一,服务于抖音各个业务。随着抖音图文、电商、IM等多图业务体量的增长,图片加载量级越来越大,对应的图片带宽成本也在日益增加。为了降低图片成本、提升用户浏览图片体验,需要持续不断的探索和优化图片能力,在保证图片展示质量的前提下,提升图片加载速度,降低图片整体成本,实现图片的 "好快省"

BDFresco简介

BDFresco是火山引擎veImageX团队基于开源Fresco拓展优化的Android端通用基础的网络图片框架,主要提供图片网络加载、图像解码、图片基础处理与变换、图片服务质量监控上报、自研HEIF软解、内存缓存策略、云控配置下发等能力,目前已覆盖到字节几乎所有App。

下面将从抖音视角出发,介绍抖音基于BDFresco在图片方向做了哪些优化。

优化思路

一张网络图片完整的加载流程如下:

客户端通过网络获取业务数据,响应内容包括对应的图片数据,通过将图片Url数据交给BDFresco加载,正式开始图片的加载流程。BDFresco会判断当前图片是否在内存缓存及磁盘缓存,若存在则执行对应解码或渲染操作,若不存在则直接走veImageX-CDN下载,将图片资源下载到本地后再进行解码和渲染操作。

图片加载过程不仅占用了客户端内存、存储和CPU等资源,也消耗了网络流量和服务端资源。

图片的加载流程本质上是一个多级缓存逻辑,可以将图片加载流程拆分成4大核心阶段,内存缓存、图片解码、磁盘缓存、网络加载,结合指标监控体系,分别针对各阶段进行优化:

优化过程

指标建设

在进行图片优化之前,需要对图片整体质量完成一次数据盘点,指标建设是至关重要的一步。通过建立指标系统,能够帮助我们了解图片现状、确定优化方向和评估优化后的效果。

BDFresco提供日志上报能力,上报的图片日志经过veImageX云端数据清洗,最终可以在veImageX云端控制台查看图片质量相关指标。从触发图片加载,到内存、解码、磁盘、网络各个阶段都建立了完备的数据监控体系,覆盖各阶段加载耗时、成功率、客户端和CDN缓存命中率、文件大小、内存占用、大图异常监控等几百项指标。

具体举措

1 内存缓存优化

1.1 内存查找优化
内存缓存原理

BDFresco是通过Producer/Consumer接口来实现图片加载的流程,例如网络数据获取、缓存数据获取、图片解码等多种工作,不同阶段由不同Producer实现类处理,所有的Producer都是一层嵌套一层,产生的结果由Consumer进行消费。一个简化后的图片内存缓存逻辑如下:

其中,读取内存或磁盘缓存是通过缓存key来进行匹配,缓存key是通过Uri做转换的,可以简单理解成cacheKey==uri,抖音在之前上线过一个缓存key优化的实验:对于同个资源的不同域名,会剔除host和query参数,即cacheKey被简化为scheme://path/name

优化方案

业务在进行图片加载时,BDFresco支持传入Uri数组,Uri均是同一资源,指向的是不同veImageX-CDN地址,实际上内部会将该批Uri(A-B-C)识别成同一个缓存key。

如下图所示,ABC3个Uri并不完全是按照【A全流程查找->B全流程查找->C全流程查找】的顺序执行,而是会先对ABC各进行一次内存缓存查找,再按顺序进行ABC的全流程查找。

由于ABC为同一资源,只是域名不同,在端上生成的缓存key一致,实际上的ABC各自的内存缓存查找为无效操作,由于该环节在UI线程执行,且抖音存在多图场景,一次滑动会触发多次图片加载逻辑,因此部分场景会导致卡顿丢帧等情况发生。

通过将多余的内存查找流程去除,对大盘帧率有明显提升。

1.2 动静图缓存拆分

抖音图片的内存缓存大小,是根据 java 堆内存大小来进行配置,默认大小为1/8,即32M或者64M。由于Android 8后,图片内存数据不再存储在java堆上,而是存在native堆,如果继续使用堆内存大小来进行图片内存缓存大小的配置是不合理的,因此通过将内存缓存大小*2,希望能减少解码操作,优化OOM和ANR指标。

实验后的稳定性指标显示,OOM虽然减少了,但是问题转换成了native崩溃和ANR都显著劣化,实验并不符合预期。

图片的缓存命中率和缓存大小成正相关,缓存大小越大,命中率越高,但随着缓存大小的增大,命中率提升空间会越来越小。

结合实验结果来看,单纯增大缓存大小会导致内存水位上升,引发ANR和native崩溃问题,方案并不可行。

目前动图和静图的内存缓存使用同一块缓存块,BDFresco的缓存管理是LRU的淘汰策略,如果播放动图帧数过多,很容易把静图缓存给替换掉,重新切换回来静图就需要重新解码,重新解码势必带来性能的损耗和用户体验的降低,抖音上存在较多此类场景,如IM、个人页动静图混搭场景。

同时,考虑到直接增大内存缓存大小,命中率提升的空间不高,所以尝试将动图和静图缓存做隔离,动静图各使用一块内存缓存,能够有效地提升命中率,减少解码操作

最终实验收益:

2 图片解码优化

2.1 解码格式优化

的内存大小图片长度图片宽度

单位像素点占用的字节数

单位像素占用的字节数由颜色模式Bitmap.Config决定,即ARGB 颜色通道,主要有6种类型:

目前抖音主要使用ARGB_8888和RGB_565两种配置,ARGB_8888支持透明通道,且颜色质量更高,RGB_565不支持透明通道,但整体内存占用少了一半,抖音的优化思路如下:

2.2 heif解码内存优化

优化原理:

BDFresco中heic图解码原逻辑是通过jni调用解码器的解码接口,返回解码后像素数据,返回到java层再转换成Bitmap对象展示。原逻辑中存在使用超大临时对象问题,会导致java内存开销以及GC,优化后减少大对象创建,直接在native层完成Bitmap对象构建,预期减少heif图片解码耗时,提升一定流畅度。

将原有heif图片解码流程从:

优化为流程:

修复前:每个heic图片解码时使用两个大数组:

修复后: 无java层大数组使用,只使用一个40K-700K的native层的DirectByteBuffer数组。减少两个java层大数组创建,减少GC发生概率以及因为大数组创建导致的OOM问题,从而带来流畅度以及ANR收益。

在抖音上开实验,性能相关指标均有显著提升:java内存占用减少,heic解码耗时减少,Android ANR减少,从而显著提升图文的消费市场,带动了整体使用时长收益。

2.3 自适应控件解码

在前面,我们提到有超过15%的图片存在一倍尺寸的浪费,导致解码阶段需要申请大量的内存,最终展示在控件上并不需要这么大的bitmap,我们通过将图片尺寸resize至控件大小后进行解码,最终解码出小分辨率的Bitmap,能够将解码内存申请极致化。

但考虑到图片浪费主要是服务端下发过大的图片,单纯在解码阶段限制大小,无法解决网络阶段的大图片问题,带宽浪费和网络加载耗时长问题仍然没有解决,因此我们将该阶段做了前置迁移,在网络加载阶段进行优化,具体方案可看4.2节按需缩放方案。

3 磁盘缓存优化

通过优化客户端的磁盘缓存配置来提升缓存命中率,减少图片请求量级,在提升图片加载速度的情况下,也能降低图片带宽成本。

磁盘缓存分为3种:主磁盘、small磁盘、独立磁盘;各磁盘空间存在上限,采用LRU替换算法,目前抖音主要使用主磁盘和独立磁盘,整体流程如下:图片默认存储在主磁盘,图片被替换概率较高;若业务指定独立磁盘cacheName,则指定图片会单独使用一个磁盘,被替换概率低。

4 网络加载优化

4.1 图片格式优化
常见图片格式
heic格式推广

当前veImageX平台支持最好的是heic编码格式,但到22年初,抖音Android端覆盖率不足50%,直接通过提升业务的heic占比能够大幅减少带宽成本,提升图片加载速度。

在做heif动图实验推广时,发现个人页UI帧率存在大幅劣化,在高低端设备均有6-8帧的帧率下降,实验无法上线,针对该问题,我们对heif动图的解码缓存逻辑进行一次优化,提出了heif动图独立缓存优化方案。

heif动图独立缓存

动图原理

在图片文件下载完成解析成字节流,动图正式播放之前,BDFresco会进行预解码,当动图正式播放时,会根据动图调度器的播放顺序将Bitmap渲染到屏幕上,并且在播放过程中会主动预解码下一帧,如当前需要播放第5帧,会同步解码第6帧率。其中预解码操作均在子线程中进行。

不同调度器的核心区别为:当子线程预解码速度过慢,下一帧需要播放的Bitmap不存在时,是继续返回当前帧重复播放,等待子线程进行解码,还是返回下一帧,直接在主线程进行解码渲染。

独立缓存

heif动图掉帧问题经过排查,发现heif动图采用了一个新的播放调度逻辑FixedSlidingHeifFrameScheduler:动图无任何预解码逻辑,在需要播放对应帧时,直接在主线程进行解码,即播放一帧解码一帧,这也导致了Heif动图在播放过程中需要在主线程占用大量CPU资源进行解码。

为什么heif动图必须在主线程解码呢?

对比其他动图支持任意帧解码,heif动图采用了帧间压缩的方式,引入了I帧P帧的概念,I帧为关键帧,包含了当前图像的完整信息,能够独立解码;P帧为差别帧,没有完整的画面数据,只有与前一帧的画面差别的数据,无法独立进行解码,解码需要依赖前一帧数据。

由于AndroidBDFresco的内存缓存为LRU替换,Bitmap随时有可能被回收,因此针对Heif动图的解码,必须严格按照动图顺序进行解码,否则会导致Heif动图播放过程中出现花屏绿屏等问题。

方案思考:

经过实验,最终采取了独立缓存方案,在取得带宽收益的同时,个人页帧率无明显劣化。

4.2 按需缩放
背景

图片加载流程最终会将解码后的bitmap渲染在控件上,当bitmap大小大于控件时,实际对用户感官并无影响,图片最终展示的像素值不会超过控件占据的空间,当图片大小 >> 控件大小时:

解决方案

在图片展示时上报对应的bitmap和控件大小,从上报的数据来看,存在大量业务请求的图片大小远大于控件。因此,需要采用一种通用的方案,在满足图片质量的前提下,客户端提供一套控件规范,根据控件大小将图片收敛至固定大小,保证图片尺寸和展示控件基本一致,同时减少图片碎片化问题。

个人页、同城、推荐等多个业务均存在双列封面场景,这里以双列封面为例子:

收益

5 异常恢复

尽管前面我们对图片的加载流程做了一系列优化,但因为抖音本身图片量级大,部分业务如电商、IM等对图片清晰度有较高的要求,且存在图片放大和长图展示等操作,业务会进行超大图加载,直接将图片直接加载进内存,单张图片内存甚至高达100M+,无论在磁盘IO阶段,还是内存解码或者Bitmap拷贝过程中均会申请大量内存,最终导致卡顿、ANR甚至OOM崩溃,因此需要一套兜底方案来解决图片OOM频发问题,提升图片加载的可靠性。

抖音在系统内存触顶时,会通过释放图片内存来缓解压力:监听系统内存的告警回调,根据不同级别释放不同大小的图片内存缓存,降低发生OOM和ANR的几率,但因大图存在,仍然存在大量OOM。

OOM兜底

内存是一个全局指标,并不能直接通过OOM堆栈确定异常原因,因为OOM发生的时候内存可能处于高水位状态,有可能申请了一个小对象就直接触发异常。但关注到崩溃中Top5的堆栈大部分和图片堆栈有关系,可以合理怀疑是App内图片频繁申请大内存导致。

因此针对高频的图片解码和内存拷贝逻辑,增加兜底逻辑,当代码发生OOM,主动catch,并通过清除图片占用的内存缓存来释放部分内存,降低内存水位:

实验结果表明,尽管部分OOM转换成native崩溃,但整体影响用户大幅下降,实验符合预期。

总结

总体来看,抖音在建设了图片的全链路监控后,根据数据分析对图片加载流程做了不少优化。

  1. 提升了图片加载速度和性能
  2. 减少了图片的总成本

从收益角度来看,大致可以分为成本优化和客户端体验优化两方面。成本收益主要是图片带宽成本的降低,体验收益体现在日活和OOM指标上,并且随着各种优化方案推广到更多的业务线,收益也在持续增加。

本文简要介绍了抖音基于BDFresco的图片优化最佳实践、经验沉淀、业务收益。由于篇幅所限,本文对探索历程、具体实现等细节内容有所省略,但仍希望能给业内同仁们一点启发或者参考借鉴。目前BDFresco已集成到火山引擎veImageX产品,对行业开放使用中,如需体验抖音同款图片优化能力,可以到火山引擎veImageX官网申请使用。

参考:火山引擎veImageX提供端到端一站式的整体图片解决方案,包含图片及素材托管、图像处理与压缩、分发、客户端编解码及图片加载SDK全链路能力,官网地址:https://www.volcengine.com/product/imagex

来源:字节跳动技术团队内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯