乐趣区

关于ios:百度APP-iOS端包体积50M优化实践二-图片优化

一、前言

在上一篇文章,咱们介绍了包体积优化的必要性、安装包组成部分和生成过程、国内外大厂 APP 包体积剖析、百度 APP 包体积优化技术计划及各项收益,本文重点讲述图片优化,解压 IPA 包后发现,百度 APP 中 asset 和 bundle 外面图片共有 94M,这是咱们重点优化的对象。

本系列文章目录如下:

《百度 APP iOS 端包体积 50M 优化实际 (一) 总览》(点击题目即可跳转)

百度 APP 采纳如下形式对不同的图片资源进行了优化:

第一、无用图片优化,解决的是随着版本迭代,一些图片曾经没有了援用关系,但还在 IPA 包中保留,开掘这部分图片并删除,这个优化是所有包体积优化我的项目中 ROI 最高的,影响范畴局限在单个组件内,品质可控,要害是进步查找无用图片的准确度;

第二、Asset Catalog 优化:应用 Asset Catalog 治理的图片能被 App Store 工具 App Thinning 解决,解决后用户只会下载匹配其设施分辨率的图片资源,从而升高了用户下载的包体积;

第三、HEIC 图片优化:跟 PNG、JPEG、WebP 相比,HEIC 图片编码格局体积缩小最小,从解码效率角度来说,跟 WebP 相比,HEIC 硬解码效率高。

二、无用图片优化

2.1 计划综述

首先获取所有图片资源,而后开发工具获取 Objective-C、Swift、xib、storyboard、html、js、css、json、plist 文件可能援用图片的动态字符串,接着后面两个汇合做 diff 即可排查未援用的图片,最初针对字符串拼接的常见 case 做二次过滤,笼罩的 case 越多准确度越高,当然也要思考 ROI。

2.2 获取所有图片

开启每个库的源码,用脚本检测所有图片及图片所属关系,为后续散发及落地优化提供方便。如果不开源码应用二进制库或者 ipa 包会带来很多麻烦,如获取 asset.car 外面的图片资源比拟艰难,同时只能晓得图片名称,不能间接获取图片属于哪一个库。开启每个库的源码后,用脚本递归遍历可获取所有图片,从图片门路可晓得所属关系,参考代码如下所示:
`
def findAllPictures(path):

pathDir = os.listdir(path)
for allDir in pathDir:
    child = os.path.join('%s%s' % (path, allDir))
    if os.path.isfile(child):
        # 获取读到的文件的后缀
        end = os.path.splitext(child)[-1]
        if  end == ".png" or end == ".webp" or end == ".gif" or end == ".jpeg" or end == ".jpg":
            print("文件" + child + "后缀" + end)
    else:
        # 递归遍历子目录
        child = child + "/"
        findAllPictures(child)`

2.3 获取可能援用图片动态字符串

在这个环节,咱们重点是要找到在代码中可能会援用图片的字符串汇合,如在 Objective- C 的.m 文件中,咱们常常用如下代码去加载图片 account\_login 来创立一个 UIImageView 对象,针对 Objective- C 的.m 文件内容,用正则过滤,匹配表达式为 @”(.*?)”,即可获取所有可能加载图片的字符串汇合。


imgView.image = [UIImageimageNamed:@"account_login"];
br

对于 Swift 文件咱们通常通过如下代码去加载图片 account\_login,加载形式齐全不一样,针对 Swift 这种文件,正则表达式应为 ”(.?)”。


let imageView = UIImageView(frame: CGRectMake(100, 10, 200, 200))
imageView.image = UIImage(named:"account_login.jpg")
self.view.addSubview(imageView)

对于 html 文件咱们通常用如下代码去加载图片,正则表达式应为 img\s+src=”‘[“‘]。

<html>
<body>
<img src="图片地址" alt="文本阐明" width=** height=**>
</body>
</html>

不同的文件加载图片的形式不同,如 Objective-C、Swift、xib、storyboard、html、js、plist、json 和 css 都不尽相同,在上面的表格,我整顿出罕用文件过滤图片应用的正则表达式。

2.4 获取未援用图片

通过 2.2 章节咱们获取的工程中的所有图片,通过 2.3 章节咱们获取代码中所有可能援用图片的动态字符串,那么对于每张图片而言,如果不在援用图片的动态字符串汇合中,这张图片可能就是未援用的图片。

2.5 字符串拼接的常见 case 做二次过滤

通过下面的环节,咱们通过全字符串匹配获取了未援用的图片,理论开发过程,有一些常见状况,代码中援用图片的名称是通过字符串拼接生成的,因而咱们须要进行二次过滤。第一种常见的 case,就是当手机反对暗黑模式时,同一个地位的图片通常会有两套,通过后缀如 light、dark 或者 day、night 来做辨别;第二种常见的 case,就是后缀是数字的图片序列,iOS 端有一种动画类型是 ImageView 加载多图动画,图片名称是由字符串和动静数字后缀拼接而成,过滤时须要笼罩如下后缀 \_%d,\_%ld,\_%zd,\_%lu。

三、Asset Catalog 图片优化

3.1 背景

Asset Catalog 是 Xcode 提供的在 iOS7 零碎开始引入的资源管理工具,将扩散在我的项目中大大小小的资源进行对立寄存和集中管理,包含但不限于 images、sprites、textures, ARKit resources 和 PDF,咱们能够把之前放在 bundle 的图片或者其余资源放入 Asset catalog 中,XCode 最初对立压缩成一个 Assets.car 的文件。

在 Asset Catalog 之前,咱们通常将图片间接放到工程的 bundle,这种形式存在一些毛病:第一、空间节约,不同设施须要不同分辨率图片,所以在 bundle 里对同一张图片同时存在二倍图和三倍图,浪费资源;第二、图片压缩只能针对单个文件,没有对立的压缩性能;第三、信息冗余,每个图片资源都会存储本人的元数据和其余的一些属性信息,如果存在很多同类型的资源,这些雷同的信息会产生冗余,造成空间节约。

3.2 Asset Catalog 长处

针对 bundle 存在的上述问题,Asset Catalog 做了诸多优化,无论是包体积优化、对立的图片压缩和便当的资源管理,还是高效的 IO 操作,每一项优化都做到了极致,上面具体介绍:

第一、包体积瘦身 :Asset Catalog 为不同类型设施(分辨率不同) 或者雷同类型设施但不同配置 (磁盘和内存不同) 提供定制化资源下载,之前在 bundle 须要放二倍图和三倍图,同一张图片最初在用户手机上会有两份,有了 Asset Catalog 后,当用户下载 App 时,只有跟用户手机硬件设施参数相匹配的资源才会被下载,其余不会下载,比如说,iphone8 手机用户只会下载二倍图片,iphone13 的用户只会下载三倍图片,这样可显著缩小下载包大小。

第二、对立的图片无损压缩:Asset Catalog 默认对文件夹中的所有图片采纳无损压缩,压缩办法是 Apple Deep Pixel Image Compression,这是苹果新引入的一种压缩模式,会依据图片的色谱个性抉择最优的算法进行压缩,压缩比能进步 15~20%。WWDC2018:Optimizing App https://developer.apple.com/videos/play/wwdc2018/227/ 有具体介绍。具体来说,针对不同类型的图片采纳有不同的优化形式,一类是简略的图片资源,如很多 icon 图片,这类图片只有绝对简略的配色和设计。另一类指的是简单的图片资源,Apple Deep Pixel Image Compression 针对这两种模式都做了不同模式的优化,图片资源体积越大应用 Asset Catalog 后优化的成果就越显著,对立的压缩更有利于实现包体积瘦身,上面这张图是苹果官网给出的 Apple Deep Pixel Image Compression 体积压缩比的优化。

第三、便当的资源管理:如果将图片间接放在工程目录上面,我的项目打包后图片文件是散落在 iPA 包外面,而如果用 Asset Catalog 来治理放在 xcassets 中,在打包后会将这些图片对立压缩成一个 Assets.car 的文件。

第四、高效的 I / O 操作:Assets Catalogs 图片加载耗时比一般的 bundle 加载图片耗时要少两个数量级,这是因为编译最初生成 Assets.car 文件蕴含了 BOM 文件,BOM 文件提供了图片加载时须要的的 rendition、renditionKey 和 attribute 属性值,rendition 是 CoreUI.framework 对某一图像资源的不同款式的统称,如 @2x,@3x,每一个 rendition 有一个 renditionKey 与之对应,通过 renditionKey 获取到对应的 attribute,attribute 中蕴含了各种属性,如图片的分辨率、垂直大小、程度大小等参数。

用 Assets Catalogs 治理的图片,通过 imageNamed 办法进行加载,因为 car 文件外面的上述资源信息是 XCode 编译时生成好的,当解析完 car 文件后,能够间接通过图片名称获取 renditionKey 和 attribute 属性并读取图片资源,没有任何多余操作,相同对于用 bundle 治理的图片,额定的操作太多导致耗时重大。对于 bundle 治理图片有两种加载形式,第一种通过 imageName 形式加载图片,须要先去 Assets.car 外面查问,因为图片资源并不在 Assets.car 外面,所以在获取 rendition 和 renditionKey 时屡次调用 canGetRenditionWithKey,canGetRenditionWithKey,最初再从新通过 mmap 加载读取图片属性和图片资源,造成 rendition 和 renditionKey,总体耗时最大;第二种通过 imageWithContentsOfFile 加载图片,不须要去 Assets.car 外面查问,没有生成 rendition 和 renditionKey 的相干操作和缓存操作,只有读取图片属性和图片二进制的操作耗时,然而没有图片属性等相干缓存所以耗时比拟长。

3.3 Assets.car 生成过程

具体来说,Xcode 在解决 Asset Catalog 节点时,构建 Asset Catalog 的工具 actool 会先对 Asset Catalog 中的 png 图片进行解码,失去 Bitmap 数据,而后再使用 actool 的压缩算法进行编码压缩解决生成 Assets.car 文件,这就能够解释在 Asset Catalog 中放 jpg 格局图片,最初生成的 Assets.car 文件中却是 png 格局图片。

3.4 Assets.car 操作

用 Assets Catalogs 治理的图片,XCode 编译时并不是简略的拷贝操作,而是将所有资源打包生成的 Assets.car 文件,这是一种压缩文件,间接解压无奈操作的,利用 XCode 自带工具 assetutil 能够剖析.car 文件,剖析命令如下所示:

sudo xcrun --sdk iphoneos assetutil --info ./Assets.car > ./AssetsInfo.js

通过 AssetsInfo.json 获取图片相干属性,然而无奈获取外面的图片。

如果想将 car 文件中的图片提取进去,举荐一个开源工具叫 Asset Catalog Tinkerer,能够从 github 下载,这里给出 github 地址:https://github.com/insidegui/AssetCatalogTinkerer

3.5 Asset Catalog 的压缩算法

应用 3.4 节介绍的 XCode 自带工具 assetutil 能够晓得每张图片的压缩算法,Compression 字段值代表不同图片的采纳的不同压缩算法,通过实际发现 actool 反对的压缩算法有 deepmap2、deepmap\_lzfse、zip、lzfse、palette\_img,具体采纳哪种压缩算法跟很多因素无关,如图片本身个性、打包的 XCode 版本、Framework 反对的 iOS 最低版本、编译配置(Asset Catalog Compiler – Options Optimization),从实际效果来看,XCode 会依据综合上述因素抉择一个压缩比最优的算法,另外这些压缩算法都是无损的。

3.6 不要做无损压缩

开发者在图片放入 Asset Catalog 之前千万不要做无损压缩,无损压缩算法是通过扭转图片的压缩编码算法达到缩小体积大小的目标,不会扭转解码后的 Bitmap 数据,从 3.3 节中咱们晓得 Assets.car 文件的生成过程中,Asset Catalog 的工具 actool 先做解码失去 Bitmap 数据,而后再编码压缩解决,针对无损压缩算法 actool 接管的 Bitmap 数据并没有扭转,所以无损压缩无奈优化包体积,UI 设计师给出的 PNG 图片如果采纳 Asset Catalog 优化就千万不要做无损压缩。

3.7 bundle 多倍图片 Asset 优化

Asset Catalog 是 Apple 在 2013 年公布的 iOS7 的零碎开始引入的,从 iOS 9 之后开始反对做资源管理,老代码 (尤其是 16 年前的代码) 都是用 bundle 的形式去治理图片,为此,咱们开发脚本专门针对 bundle 多倍图片做查看,而后采纳 Asset 优化,这种优化形式能够实现包体积立减一半,参考脚本如下所示:

def find_all_bundle_pic(app_package_path, all_pic_list):
    """将所有 bundle 图片存入 list 中"""
    pathDir = os.listdir(app_package_path)
    for child_file in pathDir:
        child_path = os.path.join('%s/%s' % (app_package_path, child_file))
        # isfile:如果 child 是一个存在的文件则返回 true,否则 (bundle、文件夹会等) 返回 false
        if os.path.isfile(child_path):
            if child_path.endswith(".png") or child_path.endswith(".jpg") or child_path.endswith(".jpeg") or child_path.endswith(".gif") or child_path.endswith(".webp"):
                if child_path.find(".bundle") > 0:
                    all_pic_list.append(child_path)
        else:
            find_all_bundle_pic(child_path, all_pic_list)

def find_opt_pic(all_picture_list,final_opt_pic_list):
    """查找 bundle 中反复的多倍图片"""
    for picture in all_picture_list:
        if picture.endswith("@2x.png"):
            prefix_2x = picture[0: len(picture) - 7]
            for picture1 in all_picture_list:
                # 前缀匹配
                if picture1 != picture and picture1.startswith(prefix_2x):
                    if (len(picture) == len(picture1) and picture1.endswith("@3x.png")) or len(picture) == len(picture1) + 3:
                        final_opt_pic_list.append(picture)
                        final_opt_pic_list.append(picture1)

3.8 有损图片压缩可缩小 Assets.car 大小

从 3.6 节咱们得悉无损压缩对于 Asset Catalog 是没有体积优化成果的,然而有损压缩可缩小 Assets.car 大小,因为 Asset Catalog 本身也会对图片进行压缩优化,所以有损压缩图片的收益没有 bundle 转 Asset Catalog 收益显著,罕用的有损压缩工具有 TinyPng 和 pngquant。

TinyPng 是一个网页版的工具,通过合并图片中类似的色彩,将 24 位的 PNG 图片压缩成 8 位色值的图片,并且去掉了图片中不必要的元数据来实现压缩。对于单张图片压缩应用十分不便,链接地址如下:https://tinypng.com/,然而如果要解决批量图片压缩,上传过程中容易呈现上传不胜利等问题,这个工具不反对自定义压缩配置。

pngquant 是一个有损的 PNG 压缩开源库,提供了命令行和源码库两种模式。将 24 位或 32 位的 RGBA PNG 图转换成 8 位 PNG 图并保留透明度通道。通过这个库的转化能够显著缩小 png 文件大小,pngquant 采纳的是本地脚本压缩,工具下载地址:https://pngquant.org,对批量压缩图片反对的比拟敌对,pngquant 反对自定义压缩品质,配置压缩品质小于 90 后压缩率会高于 TinyPng,并且 pngquant 是开源的,能够自定义,这是百度 APP 图片压缩的首选。

四、HEIC 图片编码优化

4.1 HEIC 图片编码长处

HEIC(High Efficiency Image Coding)是一种图像编码规范,它能够极大晋升压缩率,并无效减小贮存占用,自 iOS 11 和 macOS High Sierra(10.13)开始,苹果将 HEIC 设置为图片存储的默认格局,它由动静影像专家小组(MPEG)开发,并在 MPEG-H Part 12(ISO/IEC 23008-12)中定义,以下是 HEIC 图片的特点:

压缩率高:HEIC 图片比 JPEG 图片压缩率高 1.5 倍,比 PNG 图片压缩率高 3 倍,也比 GIF 图片压缩率高 3 倍。

节俭内存:HEIC 图片比 JPEG 图片节俭 20% 的存储空间,比 PNG 图片节俭 50% 的存储空间,比 GIF 图片节俭 80% 的存储空间。

解码效率高 :在 iOS 零碎中,HEIC 采纳硬解码,解码效率高,跟 WebP(软编码) 相比,是其 100 倍,但略慢于 JPEG。

保留原始图像品质:HEIC 图片采纳 H.264 和 JEP 格局压缩,能够保留原始图像品质。

反对无损放大:HEIC 图片反对无损放大,能够将图片放大两倍而不失真。

色调解决方面:HEIC 图片能够依据像素点的亮度散布主动亮度、对比度和饱和度,从而更好地还原图像的实在色调。

零碎兼容性好:咱们晓得 iOS 11 开始 HEIC 是图片存储的默认格局,也就是 iOS 11 当前的零碎都反对 HEIC 图片,但咱们百度 APP 目前还反对 iOS10 零碎,对这部分用户如何解决?在实践中发现,在 iOS10 零碎上,当把 HEIC 图片放 xcasset 文件里,最初图片也是能够失常显示的,咱们做了一番起因排查,用 Asset Catalog Tinkerer 工具解压出 Assets.car 文件,发现在 xcasset 里的 HEIC 图片,对于 iOS10 的零碎,在打包时会被零碎转化为 png 格局图片,Asset Catalog 解决了 HEIC 图片的兼容性问题。

4.2 如何生成 HEIC 图片

利用 Mac 自带性能实现 png 转 HEIC 办法:右键图片,疾速操作 -》转换图像,格局选 HEIF,抉择原图像。

优化后果如下所示,右边是 png 原图(1.6M),左边是 HEIC 图片(106KB)。

4.3 HEIC 图片应用办法

4.3.1 必须在 Asset Catalog 应用

HEIC 图片必须放在 Asset Catalog 中能力应用,bundle 形式不反对 HEIC 图片加载。

HEIC 图片的加载应用办法和一般的 asset 图片一样,如下所示:

imgView.image = [UIImage imageNamed:@"account_login"];

4.3.2 对于大图 HEIC 格局显著体积小

实践上来说,HEIC 格局图片的体积是 PNG 格局图片的三分之一,但理论过程发现对于大图,这个优化成果很显著,然而对于小图尤其是小于 10K 的图片,HEIC 图片还有可能超过 PNG 格局图片,所以咱们在做 HEIC 图片编码优化时,对于小图不倡议用这种形式。

4.3.3 带有 Alpha 通道的 PNG 图片不要做有损压缩

在实际过程中发现,一张 PNG 原图,尤其是带有 Alpha 通道,通过有损压缩 (TinyPng 或 ImageOptim) 后,再生成 HEIC 图片时,在 iOS12,13,14 零碎上会显示绿幕,所以带有 Alpha 通道的 PNG 图片不要做有损压缩,存在兼容性问题。

五、总结

图片优化是包体积优化的重头戏,百度 APP 通过两个 Q 的优化落地 9.75M 的收益解决了存量图片的问题,随后建设图片应用标准和无用图片检测流水线解决增量图片的问题。

本文具体介绍了无用图片检测计划、Asset Catalog 图片优化和 HEIC 图片优化计划,后续咱们会针对其余优化类型具体介绍其原理与实现,敬请期待。

——END——

参考资料:

[1]Asset 应用办法:https://developer.apple.com/library/archive/documentation/Xco…\_ref-Asset\_Catalog\_Format/index.html#//apple\_ref/doc/uid/TP40015170-CH18-SW1

[2]Asset 介绍:https://help.apple.com/xcode/mac/current/#/dev10510b1f7

[3]WWDC2018:Optimizing App Assets:https://developer.apple.com/videos/play/wwdc2018/227/

[4]TinyPng:https://tinypng.com/

[5]pngquant:https://pngquant.org

[6]HEIC 图片介绍:https://mobiletrans.wondershare.com/heic-convert/what-is-heic…

举荐浏览:

浅论分布式训练中的 recompute 机制

分析多利熊业务如何基于分布式架构实际稳定性建设

百度工程师的软件品质与测试随笔

百度 APP iOS 端包体积 50M 优化实际 (一) 总览

基于 FFmpeg 和 Wasm 的 Web 端视频截帧计划

百度研发效力从度量到数字化变质之路

退出移动版