iOS搜狗输入法包大小优化实践

北京中科医院假 http://pf.39.net/bdfyy/zjdy/161222/5131646.html
一、背景输入法工程经过多年迭代,添加的业务代码、三方库越来越多,同时遗留了很多无用代码、资源和未经压缩的图片,包大小越来越大。包大小有两个概念,分别是下载大小和安装大小。下载大小指用户通过网络下载的安装包大小,其本质是一个压缩文件。安装大小指用户安装包后,占用的磁盘大小。我们通过AppStore看到的是安装大小,下载大小只能开发者通过AppStoreConnect查看。通过AppStore下载应用时,下载大小越大,耗费的时间越长,出错的几率越高,下载和升级的成功率越低。如果下载体积超过AppStore的限制,在搭载iOS13以前版本的手机上,用户只能通过Wi-Fi环境下载。下载大小的限制是不断变化的,具体是iOS5.1限制50M,iOS7限制M,iOS11限制M,iOS12限制M。在搭载iOS13(含)以后的手机上,用户如果使用移动网络下载大小超过限制的包,系统会弹框提示,让用户选择继续下载或是等Wi-Fi连接后再下载。AppStore的上述限制也可能影响用户的转化率。冗余的代码、资源不但增加包大小,还会降低代码的可读性,增加日常维护成本。因此包大小需要定期优化。二、优化流程说明性能优化一般要遵循检测、定位问题,寻找解决方案,解决并验证效果的流程。如果效果不理想,需要重复上述流程。优化时切忌凭感觉在没有测量和报告的前提下直接改动,这样往往起不到好的效果,还会增加程序的复杂度,埋下bug的种子,造成后续代码维护困难。搜狗的安装包优化遵循如下流程:流程分为以下几个步骤:生成基准包:新建基准分支,编译生成基准包和其它输出物,用于静态分析,检验优化效果。静态检测生成报告:分析编译生成物和源码目录,生成包大小质量报告,帮助发现问题。找出优化点:根据报告,找出优化点,排定优先级。开始优化:针对不同优化点选择适当方案进行优化。生成优化包:优化完成后打包。检验优化效果:比对优化包和基准包,查看实际优化效果。看优化是否达到预期,达到就结束,否则跳到第二步继续检测优化。三、开始优化目前网上很多关于包大小优化的文章,总结的都比较全,但都缺乏自动化工具支撑优化流程。如果每一步都需要人工参与,势必影响开发效率,甚至导致无法长期坚持。为了解决这个问题,搜狗针对包大小优化的各个步骤研发了一套工具(其中整合了个别开源工具),以便更有效的优化。下面就结合优化流程和工具介绍下输入法的瘦身过程。1、生成基准包优化前,我们先新建一个分支作为基准分支,根据这个分支编译生成基准包和其它输出物,包括IPA文件、archive包、dSYM文件、linkmap文件等等。我们用工具分析编译输出物,生成包大小质量报告,报告会给出一些优化建议。此外,基准包也将作为基线检验后续的优化效果。搜狗的编译系统会将编译生成物输出到自定义build目录,结构如下面3张图所示,包含3个子目录:图1图2图3图1:AutoArchive子目录,目录下是archive包,后缀名为xcarchive的文件,该文件是XcodeArchive功能的生成物,功能入口在“Xcode菜单栏--Product--Archive”。Archive包其实是一个目录,包含App包(后缀为.app的目录),dSYM文件(可执行程序导出的符号文件)等。图2:IPA文件子目录,该目录包含生成的IPA文件,其它一些配置文件和log等。图3:Linkmap文件子目录,所有可执行程序生成的linkmap文件都在这个目录。Linkmap文件的介绍见下文Linkmap分析及优化部分。每个公司都有自己的打包系统,按照一定格式组织编译输出物,这里不展开赘述。但要使用优化工具的全部功能,输出物至少要包含archive包、IPA文件以及linkmap文件。注:我们需要将包上传到AppStoreConnect后台,等后台处理完成后才能查看准确的包大小,这个过程比较耗时,会降低开发效率。优化阶段我们一般基于本地包得出包大小和优化效果,估算成真实大小,等全部优化完毕后才通过AppStoreConnect查看最终效果。基准包生成后,我们进入下面的步骤。2、静态分析生成报告、分析报告及优化静态分析生成报告、分析报告和优化这3个步骤是紧密关联的,我们放在一起讨论。下面我们将从安装包、Linkmap文件、可执行程序、源码几个维度分析生成报告,并查找可以优化的点。此外,Xcode的一些编译设置项也可以帮我们缩小包体积,这一点会在后面说明。2.1安装包分析及优化安装包分析工具包含下列功能:文件分类,将包内文件按可执行程序、二进制文件、图片、图片包(如Assets.car)、文本文件等类型进行分类统计。列出每类文件的信息,包括总大小,平均大小等。针对每类文件设置阈值,列出超过阈值的文件。用户可以自定义分类信息。检测不同目录中包含的相同文件。命令行运行安装包分析工具:

machine:cwduserpython3appthinanalyze-bundlepath-opath/to/log-file-cpath/to/config-file命令释义:工具是用python写的,只支持python3。工具包的名字是appthin,analyze-bundle是包分析子命令。第一个参数可以是IPA文件,也可以是App包,还可以是包含App包的目录。-o参数指定分析报告的生成路径。-c参数指定自定义配置文件,包含自定义文件分类信息(配置文件内容下面说明)。工具输出报告如下所示:

/path/to/ipafile-or-appbundle,包含个文件,总大小:.MB(Bytes)-----------------------------------------------优化建议:重复添加的文件,冗余1.MB(Bytes)文件:filename.gif大小:.KB(Bytes)),被添加2次,分别是/filename.gif/PlugIns/extension-name.appex/filename.gif……---------------图片文件:个,大小:4.MB(Bytes),平均大小:9.KB,阈值:50.KB,可能需要优化:14个.KB(Bytes)/filename.jpq……---------------图片包:5个,大小:35.MB(Bytes),平均大小:7.MB,阈值:1.MB,可能需要优化:3个27.MB(Bytes)/Assets.car……---------------可执行程序:9个,大小:.MB(Bytes),平均大小:11.MB,阈值:0.B,可能需要优化:9个……---------------全部文件列表51.MB(Bytes)8.B(8Bytes)/PkgInfo……报告解读如下:头部信息为IPA文件或者App包的路径,安装包内文件个数,文件总大小。其次是优化建议。第一个建议是重复添加的文件,即在不同目录里,名称相同,大小相同,md5相同的文件。每条文件信息包含大小、相对于安装包的路径。接下来是图片文件的信息,包括图片文件个数,总大小,平均大小,阈值,超过阈值的文件个数等,下面还会列出超过阈值的文件,包括大小、相对路径。后面是各种类型文件的相关信息。最后是全部文件列表。通过报告,我们可以获得包内容的全貌。报告中文件的分类信息,是分析工具从内置的配置文件读取的,格式如下面JSON所示(篇幅有限,例子只包含一个可执行程序的分类信息)。用户可以用“-c”参数指定自定义配置文件覆盖默认分类或者新增分类。

{"file_types":[{"type":"exe","displayname":"可执行程序","exts":[],"size_threshold":0}......]}?根据安装包分析报告,我们可以从重复文件、大文件、图片几个角度进行优化。2.1.1重复文件安装包中一般有两种重复文件,我们需要根据业务实际情况进行优化。第一种,copy到不同可执行程序目录的相同文件。现在的iOS应用,除了主程序外,一般还包含若干扩展程序,每个扩展程序都有自己的独立目录,嵌套在主程序目录里。有时主程序、扩展程序会使用相同的文件,如字体文件、配置文件等。在开发阶段,为了方便,开发人员在Xcode里将这种资源配置到多个可执行程序,编译时这些资源会被copy到相应可执行程序的子目录,造成磁盘空间浪费。如下图所示,a-image

2x.png是一个图片文件,它被添加到CustomKeyboard和MyKeyboard两个可执行程序,这张图片在编译阶段会被copy到主程序目录和键盘程序目录。对于这种资源,我们可以把资源读取代码和资源放到动态库里,供各可执行程序使用,这一点可以结合下文的静态库合并成动态库方案一块实现。例如我们在动态库创建ResourceLoader类,即资源加载类,如下所示:implementationResourceLoader+(NSString*)resourcePath{NSBundle*bundle=[NSBundlebundleForClass:self];NSString*path=[bundlepathForResource:

"a-image

2x"ofType:

"png"];returnpath;}

end?各可执行程序用[ResourceLoaderresourcePath]方法读取共享资源。第二种,在同一个可执行程序的不同子目录里,重复的文件。输入法安装包自带了多种按键音文件,它们被放到到不同目录,每个目录都有一个1K大小的配置文件,这些文件完全相同。对于按键音配置文件,经过分析,我们最终没有优化。从报告可以看出,此类文件不多,体积很小,如果优化,不仅需要改动代码,还要改变资源提供方的工作流程,投入的成本很高,收益却很小,因此放弃。2.1.2大文件在输入法优化过程中,我们遇到了下面两种类型的大文件。二进制文件,输入法有9M左右的二进制文件是针对iOS8添加的,高版本系统用不到。我们考虑iOS8用户已经很少,且iOS8系统对键盘扩展支持不友好,一些iOS8系统造成的bug无法解决等多方面因素,经过内部讨论,最终放弃了对iOS8的支持。包大小减少了9M左右。大图片,对于使用频率不高,使用路径比较深的几个大图片,我们联系了服务端的同学,将图片改成了后下载。这项优化包大小减少了1.1M左右。2.1.3图片压缩输入法工程大概有6张图片,4多张用AssetCatalog管理,编译时打包成了Assets.car文件,0多张我们打包成了自定义格式,剩下的直接拷贝到安装包。从分析报告可以看到,图片、Assets.car等文件,总共占了40M左右(这还是经过一轮优化后的数据,优化前总共55M)。一般应用中,图片资源也是较多、体积占比较大的,需要着重、定期优化。定期优化的间隔内,业务方还会不断添加新图片。每次优化时,我们怎么知道哪些图片需要优化,哪些不需要优化,提高优化效率呢?为了解决上述问题,我们自研了一套图片压缩工具。工具的核心是使用缓存目录,缓存目录镜像缓存了源码目录图片的优化结果,即对于每个源图片,在缓存目录的相同相对目录下保存优化结果。优化时,对于每张图片,我们先用缓存进行校验,如果优化过,我们不再优化,否则才真正优化。工具每次优化前从服务器更新缓存数据,优化后再将结果提交。这样,我们就可以定期、快速的优化图片了。下面是优化命令:

machine:cwduserpython3appthinoptim-imagepath/to/local-repository--cache-remote-repositoryurl/to/image-cache-repository--cache-pathpath/to/cache--exclude-pathsPods--image-suffixespng--image-size--secret-keyskey1key2--replace-origin-opath/to/log-fileoptim-image是图片压缩子命令,它的第一个参数是本地源码目录,--cache-remote-repository参数指定图片缓存仓库远程地址(我们使用的SVN),--cache-path参数提供本地缓存路径。我们还可以用--exclude-paths参数排除一些目录(如Pods),用--image-suffixes指定需要优化的图片后缀(默认png和jpg),用--image-size参数提供一个阈值,只有大小超过阈值的图片才会优化,用--secret-keys参数指定Tinypng的密钥,--replace-origin参数指定优化后是否替换源图片,-o参数指定优化报告的输出路径。优化报告示例如下:

总文件个数,大小13.MB优化文件个数0,优化前大小0.B,优化后大小0.B优化失败文件个数1,大小31.KB命中缓存数,源文件大小13.MB,缓存文件大小13.MB-----已经优化的文件-----relative-path/to/some-image.png,优化前21.57KB,优化后17.38KB......-----优化失败的文件------relative-path/to/some-image.png,失败原因:Filetypeisnotsupported.(HTTP/Unsupportedmediatype)......?报告第一行是总图片数和图片大小,第二行是优化的文件数和优化前后文件大小,第三行是优化失败的文件个数和大小,第四行是命中缓存的文件个数和缓存大小。紧接着列出所有优化的文件和优化失败的文件。除了png和jpg,输入法工程还有不到10张gif图,我们直接使用docsmall在线工具进行压缩。说完了我们的工具,再补充下真正的图片压缩工具。工具内部使用Tinypng进行压缩,使用的是官方的python接口。Tinypng是一款有损压缩工具,压缩率比较高,图片压缩前后的效果肉眼几乎无法分辨,设计师也认可Tinypng压缩的效果。除了Tinypng,我们还考察了以下压缩方案:ImageOptim,支持无损和有损压缩。无损压缩时,压缩率没有Tinypng高,系统用pngcrush处理后反而可能变大;有损压缩时,压缩参数得自己配置,开发人员不容易配置正确。由于这两个缺点我们没有选用ImageOptim。系统压缩。压缩发生在编译阶段,压缩的文件分为两类,一类是直接copy到安装包的图片,一种是AssetCatalog图片。前一种图片,编译阶段Xcode用copypng命令处理。copypng是一个脚本,脚本内部使用pngcrush压缩图片。注意,这个命令只能处理Xcodebuildphase顶级的图片。对于buildphase目录里的图片,Xcode直接copy目录到安装包,图片是不会被copypng处理的。我们可以通过Xcodebuildsetting控制是否启用pngcrush,如下图所示(Xcode工程首次添加png图片后,buildsettings才会出现这两个选项)。这两个开关建议保持默认打开就好。Assetcatalog图片,Xcode编译阶段用actool将AssetCatalog打包成Asserts.car文件。actool是二进制文件,看不到源码。我们通过Xcode改变Assetcatalog的压缩参数,打包后发现对包体积没什么影响。因此Assetcatalog也保持默认值。2.2、Linkmap分析及优化Linkmap文件是编译过程生成的链接信息文件,每个可执行程序可以生成多个linkmap文件,每种CPU架构对应一个。文件主要描述可执行程序链接的信息,包括链接的符号--方法、属性等,包含的section,以及deadstripped的符号。Linkmap文件不会自动生成,我们需要在buildsettings里打开开并设置输出路径,如下图所示:Linkmap文件的格式大致如下:

#Path:/path/to/executable#Arch:arm64#Objectfiles:[0]linkersynthesized[1]/path/to/object-file......#Sections:#AddressSizeSegmentSection0x0040x00C5B__TEXT__text......#Symbols:#AddressSizeFileName0x0040x90[1]-[ClassNamemethod:]#DeadStrippedSymbols:#SizeFileNamedead0x08[1]8-byte-literal

?

要理解Linkmap文件内容,需要了解Mach-O文件格式,由于篇幅关系,我们不展开描述,大家可以参考这篇文章osx-abi-macho-file-format-reference(


转载请注明:http://www.kelongbinga.com/klss/7032.html

  • 上一篇文章:
  •   
  • 下一篇文章: