优化系列之App体积优化-资源优化

700 阅读9分钟

在App体积优化的3种优化方向当中,资源优化应该是优化力度最大的一个方向,直接上干货

资源优化方法:

我们可以从以下3个方向进行排查,大部分的优化方式都是基于这3个方向去考虑。

(1)图片压缩
(2)将图片放置到云端
(3)排查和清除冗余图片

1. 无用图片筛选

代码层面

有源码的条件下:根据各种类型的源文件,通过正则表达式获取使用的图片集合,扫描获取所有图片名集合,取所有图片集合和使用图片集合差值获取无用图片集合

二进制集成的条件下:通过安装包来扫描无用图片

可执行文件通过 otool -v -s __TEXT __cstring 获取可执行文件中的 __TEXT.__cstring 段。__cstring 包含了可执行文件中的字符串常量(源码中的 @“xxx” 字符串);
不可读文件(.nib、.storyboardc).nib 和 .storyboardc 分别是 xib 和 storyboard 的构建产物。ibtool 是xib 和 stroyborad 的编译工具,通过 man 查看 ibtool 的具体使用方法发现:--flatten NO --compile 组合使用的时候可以生成可运行、可执行的 .nib 和 stroyboardc 文件。
可执行性文件、不可读文件确定处理方法后开发工具筛选,思路如下:
1)针对不同的文件,使用 otool、正则和直接读取将获取到的内容拼接成引用图片的超字符 str,遍历所有图片名是否被 str 包含;
2)如果包含直接过滤;
3)如果不包含,再判断是否是 image_%ld 相似图片过滤;
4)开发人员确定无误后删除,存在部分字符串拼接被误扫的,添加白名单过滤。

第三方工具

使用一个开源的Mac app,LSUnusedResources,来进行冗余图片的排查: github.com/tinymind/LS…

这个app的原理是,对某一文件目录下所有的源代码文件进行扫描,用正则表达式匹配出所有的@"xxx"字符串(会根据不同类型的源代码文件使用不同的匹配规则),形成“使用到的图片的集合”,然后扫描所有的图片文件名,查看哪些图片文件名不在“使用到的图片的集合”中,然后将这些图片文件呈现在结果中。

2. 大资源优化

  • 千万不要将大图随意拖到工程中
  • 删除无用或者可以使用其他方案替换的图片;
  • 优先转网络下载,使用默认图/纯色兜底;
  • 不能转下载的使用压缩过的jpg格式图片。
  • 不能使用jpg的图片经过压缩后( 主要是tinypng有损压缩)后放到 bundle 中使用。

3. 将图片放置到云端

那么什么图片是可以放到云端的呢?

  • 功能性引导图
  • 背景图:如页面背景。
  • 标签,提示类的图片。
  • 其他入口较深的图片。

苹果也为我们提供了方案,那就是On Demand Resource,简单介绍一下

苹果从iOS 9开始引入了On Demand Resource功能,即一部分图片可以被放置在苹果的服务器上,不随着app的下载而下
载,直到用户真正进入到某个页面时才下载这些资源文件。
我们考虑可以让某些业务仅在iOS 9及以后版本中可用,然后应用On Demand Resource来优化这些业务的资源。

划重点是你的App的最低版本是支持iOS9。如果你的App不在此范围,可以考虑如下方案:

 云端下载的策略为:
(1)在若干时机尝试下载zip图片包,对zip包进行版本判断,若云端有更新版本,则根据屏幕是3x还是2x,下载对应的zip包,解压存入沙盒中;
(2)在读取图片时,首先从bundle中读取,若失败,则从沙盒中读取,若依然失败,则将该图片当作一个网络图片进行请求,确保图片能被展示。

同时云端的图片可以从3方面进行优化

  • 模块内内置默认配置文件,支持对不同分辨率的机型加载对应的图片。
  • 支持图片url的在线更新。
  • 支持基于cdn的图片降质、webp压缩。

4. 图片压缩

图片压缩-png&jpg

图片压缩可以参考工具ImageOptim。 需要注意的是,图片压缩对于png格式的图片,效果不明显,理由如下:

  Xcode在构建的过程中,有一个步骤叫做compile asset catalog。在这个步骤中,Xcode会自行对png图片作压缩,
  并且会压缩成能够快速读取渲染的格式。如果我们对工程中的图片进行了ImageOptim的压缩,在compile asset 
  catalog的过程中,Xcode会用自己的算法重新压缩,而这个”重新压缩“的过程,相当于将ImageOptim的压缩“回滚“了,
  很可能反而增大了图片。

但是对于压缩jpg图片还是有效的。需要了解的是在没有强要求图片的情况下,相同格式的jpg的图片是要小于png的。

那么是不是真的没有办法通过压缩png的形式去进行图片体积缩小呢,答案肯定是有的,但是有一些限制条件。 针对iOS8以上,通过cocoapods管理的资源文件,还是可以通过构建的形式进行png的压缩的,方法如下:

实际上如果是通过cocoapods管理的资源文件,真正有效执行asset catalog编译的过程是在[CP] Copy Pods Resources这个脚本中。
这个脚本主要是利用actool去修改压缩策略,可以通过修改脚本去进行png图片压缩。

图片压缩-webP替代png

部分比较重的图片可以采用废弃asset catalog方式加入资源 废弃asset catalog可能能带来以下两个收益:

(1)可以考虑将png图片切换到webP等其他格式
(2)废弃Asset Catalog后,可以删去2x的图片,只保留3x的图片。

考虑到app从asset catalog中读取图片可能比从bundle中读取图片有更高的性能,所以在开发过程中,启动阶段的图片依然被保留在了asset catalog中。但是依然可以对asset.car文件进行一定量的优化。

5. App Slicing

App Slicing是iOS9增加的功能。当用户从app store上下载app时,可以只下载适用于其设备的app架构版本和所需资源,从而减少app所占的空间。

如果开发者想要使用app slicing,只需要将资源文件用Asset Catalog管理,不需要做额外的任何事情。

6.IconFont

如果你的应用有各种各样的icon图标占了不少的数量,那么你可以考虑IconFont,IconFont 可以解决因为icon大小,颜色不同而重新切图的窘境。

IconFont(https://www.iconfont.cn) 技术起源于Web领域的Web Font技术。随着时间的推移,网页设计越来越漂
亮。但是电脑预装的字体远远无法满足设计者的要求,于是Web Font技术诞生了。一个英文字库并不大,通过网络下载字
体,完成网页的显示。有了Web Font技术,大大提升了设计师的发挥空间。

IconFont优点:

  • 矢量,缩放不失真。
  • 可以设置颜色。
  • 接入成本低,不需要引入额外的类库。

7. 修复cocoapods带来的图片重复合并问题

大型App现在都使用组件化的形式进行开发,那么我们避不可少的就是使用cocoapods进行组件化的管理,那么cocoapods带来的问题也随之而来。

1) png文件和asset catalog重复合入安装包

**问题产生原因:**这个pod库在编写podspec的时候,用了这样的语句指定资源文件:

s.resources = [JDRouter/**/*.{png,plist,jpg,xib,xcassets}]

这种写法会导致asset catalog中的图片,既作为asset catalog被合并到主工程的asset.car中,也会作为png被拷贝到安装包中。导致其中一套图片白白占用了安装包空间。

**解决方案:**使用通配符来指定pod库中的资源文件显然是不合理的,会带来不可预期的陷阱。应该以白名单的形式明确指定哪些资源文件是pod库中有效的资源文件。

2) cocoapods暴力合并工程内asset catalog问题

**问题产生原因:**这个pod库在编写podspec的时候,用了这样的语句指定资源文件:

s.resource_bundles = [JDRouter/Assets/Image.xcassets]

这种写法有可能导致这个Image.xcassets中的图片,既作为了一个单独的asset.car被放入了名为JDRouter的bundle中,又被合并到了主工程的asset.car中,而后者是预期之外的。这导致这些图片在安装包中存在了两份。

原因就是cocoapod在工程构建的时候,会执行一个Copy Pods Resources的步骤,该步骤就是执行一个Pods-NewsInHouse-resources.sh脚本,脚本内容在pod install的时候生成。如果工程符合某些条件,则找到工程目录下所有的xcassets,使用xcode的actool工具将这些xcassets合并为一个assets.car文件。不管这个xcassets针对的是哪个target,是否被工程使用了,只要它在工程的某个子文件夹下,就会被打包进安装包中。

**解决方案:**在build phase中,在执行copy pods resources之前,执行一个脚本,替换Pods-NewsInHouse-resources.sh脚本的某一行,用更合理的合并方式取代暴力合并。

什么是更合理的合并方式呢?就是不再找出工程根目录下所有的xcassets,而是利用xcodeproj工具找出当前target的build phase中的copy bundle resources中的所有xcassets。也就是说,我们不再暴力合并工程根目录下所有的xcassets,而只是合并当前target需要的xcassets。

8. 利用tint color精简单色图标

该方案类似于IconFont,主要是为了精简图标,但是这个方案对于已经成型的项目浪费人力物力,时间耗时较长,可以当做一个方向去考虑,并不推荐。

tint color是苹果在iOS7推出的功能,我们可以读取一个图标,然后给它赋予一个color值,在手机屏幕上它就能显示出
相应的效果。tint color适用于对单色图标进行着色,相比于其他精简图标的解决方案,tint color方便、可靠、拥有
原生支持。

总结一下:

资源的优化,主要着重于细节规范

参考文档:

  1. App Thinning in Xcode
  2. iOS APP Slicing 实践
  3. actool man page
  4. 干货|今日头条iOS端安装包大小优化—思路与实践
  5. iOS Asset Catalog and Bundle