一、前言
在上一篇文章,咱们介绍了包体积优化的必要性、安装包组成部分和生成过程、国内外大厂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端视频截帧计划
百度研发效力从度量到数字化变质之路