淘宝 Android 端图片体验优化实践

简介: 淘宝 Android 端图片体验优化实践

1.gif

Android 端图像加载、显示、处理、监控集一体的一站式解决方案。


背景

Phenix  图像加载体系是立足手淘面向阿里巴巴全集团打造的一款 Android  端集图像加载、显示、处理、监控于一体的一站式解决方案。基于高可用、高性能、可高度定制、数据服务、省流量五大核心优势而被集团各个业务接入使用,经过长期的迭代与维护进入了相对成熟和稳定的模式。

在如今更加追求体验的背景下,对图片库提出的要求也越来越高。而图片加载长尾问题,特别是在中低端机和弱网条件下体现的更加明显。因此我们以手淘为出发点,基于用户舆情反馈以及图片库自身埋点数据,对影响图片正常展示的长尾问题进行了较为完整的梳理。

据统计,淘宝 Android 端每月图片类舆情在几百条,基本都表现出白屏、图片加载失败的现象。其中明确图片加载慢的 case 下,基本有以下几个因素:

  1. 用户网络慢,图片下载耗时久
  2. 磁盘缓存读取耗时,存在读取阻塞未返回的情况
  3. 图片尺寸大,原图访问 RT 耗时久

问题发现

结合舆情日志,通过图片库埋点数据统计发现:

  1. 图片加载磁盘读取慢(超过 N 秒) 次数每日百万次以上
  2. 图片库内部线程调度慢 (超过 N 秒)  次数每日千万次以上
  3. 图片网络错误类中,超时和连接错误每日次数亿次以上
  4. 首页信息流图片秒开率低,主要受制于原图访问问题

深入剖析场景根因

 磁盘读取阻塞

磁盘缓存读取作为图片加载流程中非常关键的一个环节,发生阻塞时后续联网下载图片资源、图片解码、图片上屏等流程会被动等待,在用户视角即为图片未加载或页面白屏。

image.pngimage.gif

 线程调度频繁

图片库将图片的请求分割到多个阶段  1. 内存缓存查找;2. 本地图片处理;3. 磁盘缓存查找;4. 网络请求;5. 图片解码;6. 图片回调上屏。

每个阶段都会进行线程切换,这些任务都共享一个大的线程池,频繁的线程切换会增加性能损耗。


image.png

image.gif

 网络任务队列阻塞

图片库内部针对网络任务(包括网络连接和网络数据下载)制定了特殊的调度器,该调度器限制了在强弱网环境下允许被传递到线程池中的最大任务数。

而弱网条件下图片数据返回慢,后续图片请求任务被阻塞在网络调度器的内置队列中,而不会进入线程池等待被执行,图片加载流程会因为排队而阻塞在队列中,上屏时间被延后,影响用户体验。

image.png

 网络下载等待

图片网络数据下载受限于网络速度,弱网条件下数据返回慢。在图片库共享线程池时,网络下载任务占用着线程不释放,会造成两个比较明显的问题:

  1. 当前图片加载流程被阻塞
  2. 线程池中线程被占用,后续图片请求需要排队等待

image.png

 图片尺寸大&原图访问

选取 JPG/PNG/WEBP/HEIC 几种图片在最近一个月的整体加载时长,从图中我们可以看出与其他图片格式相比,HEIC格式图片的整体耗时是最短的。这对于用户体验,特别是在中低端机上的体验是有明显的差距的。

非 HEIC 图片在 CDN 命中率低,绝大多数情况下会访问原图。同时图片尺寸大,再加上图片库内部的调度策略受网络资源下载时长的影响,会加剧单张图片加载总耗时。

image.gifimage.png

此外,Android 12 引入了对使用 AV1 图片文件格式 (AVIF) 图片的支持,这种格式可以显著提升相同文件大小下的图片质量,而图片库目前还不支持。

image.gifimage.png


分场景优化

基于以上提出的几类比较明显的限制根因,我们为图片上屏的场景分别制定了优化方案,并通过解决图片加载长尾问题来提升用户体验。

 统一存储空窗优化

针对磁盘缓存读取阻塞问题,图片库增加存储读取异常检测与异常状态恢复检测方案。

  1. 磁盘读取耗时检测:
    预设定磁盘读取阻塞的条件(可以通过云端配置平台动态调整)
    在连续多次检测到磁盘缓存读取任务被阻塞时主动跳过二级缓存,后续图片请求全部走网络请求
  2. 磁盘缓存读取跳过状态恢复检测:
    预设定恢复二级缓存读取的条件(可以通过云端配置平台动态调整)
    在跳过磁盘读取一段时间后,同时发起多个磁盘缓存读取任务,并且在规定时间内正常返回时恢复二级缓存读取,后续的图片加载流程与初始策略一致

image.png

 线程调度方案改造

  • 优化点1: 减少线程切换

重新梳理各任务之间的关联情况以及各任务在执行时的耗时瓶颈点,最大可能的降低线程切换的频率。

  1. 仅保留两次线程调度
    主线程切换至工作线程
    网络库线程切换至图片库线程

image.gifimage.png

  • 优化点2: 去掉网络调度内部阻塞


网络调度器的存在主要是为了限制在强弱网环境下允许被传递到线程池中的最大任务数,避免出现网络任务占满线程池,而后续可以从缓存中获取的图片也无法上屏显示的情况。
基于 优化点3 中网络数据下载变更为分片回调,不再占用图片库线程。此时图片库内部的网络调度器就不再被使用,该问题也会被规避。

  • 优化点3:网络数据分块接收

以一种空间换时间的方式,解决弱网条件下网络数据返回慢所导致的线程占用问题。

  1. 网络库首次回调创建指定大小的容器,之后分段接受图片数据,直到所有数据全部返回后切换到图片库线程执行后续任务
  2. 弱网条件下返回数据慢也不影响图片库中线程的使用,通过提高线程使用率,降低等待时间来缩短图片加载时长

image.png


 尺寸更小的图片格式

Android  12 引入了对使用 AV1 图片文件格式 (AVIF) 图片的支持。AVIF 是一种使用 AV1  编码的图片和图片序列的容器格式。利用了视频压缩的帧内编码内容。与以前的图片格式(例如  JPEG)相比,这种格式可显著提升相同文件大小下的图片质量。

采用新一代 AV1 图片格式,图片的压缩率相比 HEIC 能提高约 25%,能够在降低图片尺寸的同时缩短图片下载时长,提升用户体验。

通过与图片空间合作,在图片库内部增加了 AVIF 图片格式的支持和适配。目前已经历了 3 次完整的独立灰度,后期会先在手淘中落地并逐渐放量。

image.png

总结

磁盘缓存阻塞问题逐步放量至全量上线后优化效果明显,每周舆情数相比优化初期降低超过50%,磁盘读取耗时超过 N 秒样本数降低90%;

image.gifimage.png

线程调度优化方案已经独立灰度完成,从灰度数据看线程调度慢 (超过 N 秒) 占比小于 0.01%;预计上线后与优化前相比每日能减少 90% 的样本数;

image.gifimage.png

AVIF 图片格式独立灰度后图片尺寸与HEIC相比优化 15%,解码耗时基本保持一致,由于目前CDN 命中率较低,RT 耗时较长,需要等放量以后整体优化效果才能体现。同时后续压缩率与HEIC 相比预期达到 25%,会根据实际的埋点数据来决定后期的放量节奏。

我们还能做什么?

 图片格式收敛至HEIC(后续可统一升级到AVIF)

目前 Native 页面中 HEIC 图片占比约 60%,而 H5 页面中只有部分资源适配了 HEIC。

  1. 在 Native 页面,使用图片库提供的组件或者调用图片库提供的 CDN 适配策略时,图片库会对传入的 URL 做处理,按照图片空间的规则拼接上 HEIC 后缀,这样当 URL 到达 CDN 时,如果有HEIC 图片缓存直接返回,如果没有会请求图片空间的服务将原始图片转码成 HEIC 图片
  2. H5 页面中由于 UC 内核提供了外接三方解码器的能力,同时手淘支持远程 SO 的下载,在这两个前提下 H5 页面的图片使用图片库提供的 CDN 策略,以及图片库提供的外置解码器来加载 HEIC 图片

image.gifimage.png


针对 Native 页面可以推动业务方使用图片库提供的组件,或者主动调用图片库提供的 CDN 策略来接入对 HEIC 图片的支持。

针对 H5 页面正在和业务方做沟通以及制定更加完善的替换方案,争取早日完成图片格式的收敛。

写在最后

对于图片上屏体验而言,Phenix 图片库任重而道远。在一轮又一轮的技术迭代下,手淘的图片上屏体验在不断的优化和提升。希望在未来,图片库在优化自身的同时能够结合网络库、图片空间等模块进一步挖掘隐藏在各个环节的性能瓶颈,帮助提升用户体验。

相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
2月前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
17天前
|
搜索推荐 Android开发 开发者
安卓应用开发中的自定义控件实践
在安卓应用开发的广阔天地中,自定义控件如同璀璨的星辰,点亮了用户界面设计的夜空。它们不仅丰富了交互体验,更赋予了应用独特的个性。本文将带你领略自定义控件的魅力,从基础概念到实际应用,一步步揭示其背后的原理与技术细节。我们将通过一个简单的例子——打造一个具有独特动画效果的按钮,来展现自定义控件的强大功能和灵活性。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往更高阶UI设计的大门。
|
3月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
72 20
Android经典面试题之图片Bitmap怎么做优化
|
2月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
64 4
|
1月前
|
前端开发 Android开发 UED
安卓应用开发中的自定义控件实践
【10月更文挑战第35天】在移动应用开发中,自定义控件是提升用户体验、增强界面表现力的重要手段。本文将通过一个安卓自定义控件的创建过程,展示如何从零开始构建一个具有交互功能的自定义视图。我们将探索关键概念和步骤,包括继承View类、处理测量与布局、绘制以及事件处理。最终,我们将实现一个简单的圆形进度条,并分析其性能优化。
|
1月前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
2月前
|
前端开发 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的世界里,自定义控件如同画家的画笔,能够绘制出独一无二的界面。通过掌握自定义控件的绘制技巧,开发者可以突破系统提供的界面元素限制,创造出既符合品牌形象又提供卓越用户体验的应用。本文将引导你了解自定义控件的核心概念,并通过一个简单的例子展示如何实现一个基本的自定义控件,让你的安卓应用在视觉和交互上与众不同。
|
2月前
|
测试技术 数据库 Android开发
深入解析Android架构组件——Jetpack的使用与实践
本文旨在探讨谷歌推出的Android架构组件——Jetpack,在现代Android开发中的应用。Jetpack作为一系列库和工具的集合,旨在帮助开发者更轻松地编写出健壮、可维护且性能优异的应用。通过详细解析各个组件如Lifecycle、ViewModel、LiveData等,我们将了解其原理和使用场景,并结合实例展示如何在实际项目中应用这些组件,提升开发效率和应用质量。
52 6
|
3月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
151 3
|
3月前
|
Java Android开发 UED
安卓应用开发中的内存管理优化技巧
在安卓开发的广阔天地里,内存管理是一块让开发者既爱又恨的领域。它如同一位严苛的考官,时刻考验着开发者的智慧与耐心。然而,只要我们掌握了正确的优化技巧,就能够驯服这位考官,让我们的应用在性能和用户体验上更上一层楼。本文将带你走进内存管理的迷宫,用通俗易懂的语言解读那些看似复杂的优化策略,让你的开发之路更加顺畅。
75 2