限制灵活性,减少layout文件减少layout文件有两个方法:复用和融合(include)。 复用layout文件把一些页面共用的布局抽出来,这无论是对layout文件的管理还是瘦身都是极为有用的。 就比如说任何一个app的list页面是相当多的,从布局层面来说就是一个ListView或者RecyclerView,其背后还可能会有loading的view,空状态的view等等,所以我的建议是建立一个list_layout.xml,其余的list页面可以复用或者include它,这样会从很大程度上减少layout文件的数目。 融合layout代码对于可以被复用的layout我们可以做统一管理,但是对于不会被复用的layout怎么办呢?假设一个页面是由两个区域组合而成的,fragment的做法是一个页面中放两个container,然后再写两个layout,但实际上这两个layout经常是没有任何复用价值的。我希望找到一种方式,在view区块还没有复用需求的时候用一个layout搞定,需要被复用的时候也可以快速、无痛的拆分出来。 1. UiBlock UiBlock是一个类似于fragment的解耦库,它可以为同一个layout中不同区域的view进行逻辑解耦(因为layout可预览的特性,ui定位方面不是难题),它能帮我们尽可能少地建立layout文件。 如果未来需求发生了变动,layout文件中的一块view需要抽出成独立的layout文件的时候,UiBlock的逻辑代码几乎不用改动,你只需要把抽出的layout文件include进来,然后在 2. ListHeader 我将listView和它的没有复用价值的header放到了同一个layout中,然后在activity中利用上述代码进行了操作,最终完成了用一个layout文件给listView加头的工作。这段代码我很久没动过了,有利有弊,放在这里我也仅仅是举个例子,希望可以帮助大家扩展下思路。 动态下载图片做过滤镜和贴纸的同学应该会注意到贴纸、表情这类的东西是相当大的,对于这类的图片资源我强烈建议通过在线商店进行获取。这样既可以让你踏踏实实的卖贴纸,又可以减小应用的大小。这么做虽然有一定的复杂度和出错概率,但投入产出比还是很不错的。 准确放置不同分辨率的图片这个虽然不算是app大小的优化,但是如果你放错了图片,对于app启动时的内存大小会有一定的影响:
国内也有很多人说可以用一套图片来做,不用出多套图,借此来达到app瘦身和给设计减负的目的。谷歌官方是建议为不同分辨率出不同的图片,为此国内也有不少文章讨论过这件事情,这篇总结的不错推荐一读。 每次说到这个话题的时候总有很多人有不同的看法,况且很多人还不知道.9图也是需要切多份的,所以这里我还是先分析一下大厂的放图策略,最后咱们再讨论下较优的方案。 分析过程见:《淘宝、微博、微信的 Android 图片放置策略》
通过分析得出,传统的出多个分辨率图片的做法在大厂中已经发生了改变,阿里系、腾讯系的产品都采用了一套图走天下的路子。这样的做法还是有利有弊的,权衡之下我给出如下建议:
成年人不看对错,只看利弊,所以还请大家权衡一二。 丢弃特定资源如果我们希望保留或丢弃特定的资源,需要在项目中创建一个 两个属性都可以使用逗号 keep: discard: 开启严格模式开启严格模式后,可能对于编译会产生一些问题,警告也会增多,所以需谨慎开启此功能。
Gradle Console中的日志中也会有移除APK资源的信息: apk构建完成后会Gradle会在 例如:找出为什么 要 为什么 此部分的内容大量参考自《Shrink Your Code and Resources》一文,请移步官网去详细了解。 移除第三方库中的配置文件有时候引用的三方库会带有一些配置文件xxxx.properties,或者license信息,打包的时候想去掉这些信息,就可以这样做 优化图片对于图片的优化应该是放在优化res一节中进行讲解的,但是因为图片这块比重太大了,所以我让其独立成为一节。 选择合适的图片格式想要做好图片的优化工作首先要知道应该选择什么样的图片格式,对于这点我推荐一个视频,方便大家进行深入的了解。 这是谷歌给出的建议是:VD->WebP->Png->JPG
使用VectorDrawableVD即VectorDrawable,是android上的svg实现类。在经历了长达半年的缓慢兼容之路后,现在终于被support库兼容了,官方文档中给出了这样一个例子: 配置好后,我们就可以利用强大的svg来替换纯色icon了。 因为svg矢量图的优势,终于可以通过一套图适配多个机型了。svg的好处有很多,缺点也不少,关于svg的优缺点和实践方案,建议移步:http://todo(未写完) 使用WebPwebp是一种新的图片格式。从Android4.0 开始原生支持,但是不支持包含透明度,直到4.2.1 才支持显示含透明度的webp,使用的时候要特别注意。
我们可以通过智图或者isparta将其它格式的图片转换成webP格式。 关于webP的优缺点和实践方案,建议移步到《WebP的问题和解决方案》继续阅读。 复用图片利用现有的图片进行复用是一个相当有用的方案,关于复用的原则建议和设计进行讨论,当设计师认为二者均为同一图形的时候才可被认为可复用。 复用相同的icon我们通过svg可以让一张图片适用于不同大小的容器中,以达到复用的目的。最常见的例子就是“叉”,除非你的x是有多种颜色的,那么这种表示关闭的icon可以复用到很多地方。 上图中我通过组合的方式将长得一样的icon(facebook、renren等)复用到了不同的界面中,不仅实现了效果,可维护性也不错。 使用Tint着色器(tint)是一个强大的工具,我将其和shape、svg等结合后产生了化学反应。 TintMode共有6种,分别是:add,multiply,screen,srcatop,srcin(默认),src_over。 一般用默认的模式就可以搞定大多数需求了,使用到的控件主要是TextView和ImageButton。ImageButton官方已经给出了支持方案,TextView因为有四个Drawable,官方的tint属性在低版本又不可用,所以我让SelectorTextView支持了一下。如果你想要了解具体的兼容方法,可以参考代码或《Drawable 着色的后向兼容方案》。 ImageButton SelectorTextView 因为我用了SelectorTextView和SelectorImageButton,所以我对于背景的tint没有什么需求,也就没做兼容性测试,有兴趣的同学可以尝试一下。 如果你决定要采用tint,一定要通过云测等手段做下兼容性测试,下图是我对于上述属性的测试结果: 完美兼容 复用按压效果一个应用中的list页面都应该做一定程度的统一,对于有限长度的list,我们可能偏向于用ScrollView做,对于无限长的list用RecyclerView做,但对于它们的按压效果我强烈建议采用同一个样式。 以微信为例,它的所有列表都是白色的item,我的优化思路如下:
通过旋转来复用如果一个icon可以通过另一个icon的旋转变换来得到,那么我们就可以通过如下方法来实现: 这种方法虽好,但是不要滥用。需要读代码的人具备这种知识,否则会出现不好维护的情况。而且在设计师真的是认为两个图有如此的关系的时候才可这样实现,万不可耍小聪明。 压缩图片图片的压缩策略是:
关于如何量化两张图片在视觉上的差别,Google 提供了一个叫butteraugli的工具,有兴趣的同学可以尝试一下。 关于更加详细的内容可以参考:《smallerapk-part-6-image-optimization》和《QQ音乐团队的PNG图片压缩对比分析》 ImageOptimmac上超好用的图片压缩工具是ImageOptim,它集成了很多好用图片压缩库,很多blog中的图片也是用它来压缩的。 值得一提的是,借助Zopfli,它可以在不改变png图像质量的情况下使图片大小明显变小。 pngquantpngquant也是一款著名的压缩工具,对于png的疗效还不错。它不一定适合app中那种背景透明的小icon,所以对比起tinypng来说,优势不明显。 数据来自:http://www.jianshu.com/p/a721fbaa62ab tinypngtinypng是一款相当著名的商用压缩工具,tinypng提供了开放接口供开发者开发属于自己的压缩工具(付费服务)。tinypng对于免费用户也算友好,每月可以免费压缩几百张图片。 我通过TinyPic来使用tinypng,更加简单方便。我一般是发版本前才做一次图片压缩,每次debug的时候是直接跳过这个task的,完全不影响日常的debug。 有人说tinypng的缺点是在压缩某些带有过渡效果(带alpha值)的图片时,图片可能会失真,这时你可以将png图片转换为webP格式的图片来解决此问题。 注意事项aapt可以在构建过程期间优化放置在 请记住,aapt有以下限制:
如果你自己做了图片压缩,那么请使用 优化dexdex本身的体积还是很可观的,虽说代码这东西不占用多少存储空间,但是微信这样的大厂的dex已经达到了20多M。我大概估计了一下,如果你没有达到方法数上限,那么你的dex的大小大约是10M,可没有用multiDex的又有几家呢? 记录方法数和代码行数dexcout要优化这个部分,你首先要对公司的、android库的、第三方库的代码进行深入的了解。我用了dexcount来记录项目的方法数: 通过分析后你可以得出很多有用的结论,比如某个第三方库是否已经不用了、自己项目的哪个包的方法数最多、目前代码情况是否合理等等。 statistic我是通过 预览 现在我可以知道:
apk method你还可以用apk-method-count这个工具来查看项目中各个包中的方法数,它会生成树形结构的文档,十分直观。 利用Lint分析无用代码如果你想删掉没有用到的代码,可以借助as中的 search action Lint是一个相当强大的工具,它能做的事情自然不限于检查无用资源和代码,它还能检测丢失的属性、写错的单位(dp/sp)、放错目录的图片、会引起内存溢出的代码等等。从eclipse时代发展到现在,lint真的是越来越方便了! Lint的强大也会带来相应的缺点,缺点就是生成的信息量过多,不适合快速定位无用的代码。 我推荐的流程是到下图中的类目中直接看无用的代码和方法。 注意: 通过proguard来删除无用代码手动删除无用代码的流程太繁琐了,如果是一两次倒还会带来删除代码的爽快感,但如果是专人机械性的持续工作,那个人肯定是要疯的。为了保证每次打包后的apk都包含尽可能少的无用代码,我们利用一下proguard这个强大的工具。 虽然这种方式成果显著,但也需要配合正确的proguard配置才能起作用,推荐看下《读懂Android中的代码混淆》一文。 这种利用混淆来删除代码的方式是一种保险措施,真正治本的方法还是在开发过程中随手删除无用的代码,毕竟开发者才是最清楚一段代码该不该被删的。我之前就是随手清理了下没用的代码,然后就莫名其妙的不用使用mulitdex了。 剔除测试代码我们在测试的时候可能会随便写点测试方法,比如main方法之类的,并且还会引入一些测试库。对于测试环境的代码gradle提供了很方便的androidTest和test目录来隔离生产环境。 对于测试时用到的大量库,可以进行test依赖,这样就可以保证测试代码不会污染线上代码,也可以防止把测试工具、代码等发布到线上的错误(微博就出过这样的错误)。 PS:在layout中利用 区分debug/rtm/release模式debug模式是开发者的调试模式,这个模式下log全开,并且会有一些帮助调试的工具(比如:leakcanary,stetho),我们可以通过 debug和release是android本身自带的两种生产环境,在实际中我们可能需要有多个环境,比如提测环境、预发环境等,我以rtm(Release to Manufacturing 或者 Release to Marketing的简称)环境做例子。 首先在目录下创建rtm文件: 复刻release的配置: 配置rtm依赖: rtm环境自然也有动态替换application文件的能力,我为了方便非开发者区分app类别,我做了启动icon的替换。 现在我可以将环境真正需要的代码打包,不需要的代码全部剔除,以达到瘦身的目的。 使用拆分后的support库谷歌最近有意将support-v4库进行拆分,可无奈v4被引用的地方太多了,但这不失为一个好的开始。目前来看使用拆分后的support库是没有什么优点的,所以我也不建议现在就开始动手,当谷歌和第三方库作者都开始真的往这方面想的时候,你再开始吧。 减少方法数,不用mulitdexmulitdex会进行分包,分包的结果自然比原始的包要大一些些,能不用mulitdex则不用。但如果方法数超了,除了插件化和RN动态发包等奇淫巧技外我也没什么好办法了。 使用更小库或合并现有库
话人人都会说,但如果一个项目是由多个项目成员合作完成的,是很难避免重复引用库的问题的。同一个功能用不同的库,或者一个库用不同版本的现象比比皆是,这也是很难去解决的。我的解决方案是部门之间多沟通,尽量做base层,base层由少数人进行维护,正如微信在so库方面的做法:
四、总结app的瘦身是一个长期并且艰巨的工作,如果是小公司建议一两个月做一次。大公司的话一般都会对app的大小进行持续的统计和追踪,瘦身工作会有专人负责。总之,希望大家在阅读完本文后可以着手对项目进行优化工作,带来真正的收益。 觉得文章对你有帮助的童鞋可以关注下方的公众号 GitHub小伙伴 ,同时动动手指转发给其他需要的童鞋们吧。 |
|