随着业务的不断增长,网易新闻客户端已经承载了过多的功能,不仅仅是一个新闻资讯的app,而是成了公司各个业务线的载体。因此,体积增长的问题就暴露了出来。体积最大时达到了30M,这对一个android应用级别的app来说算是非常大的了。于是不得不把瘦身的工作提到日程上。

分析apk,查看个模块的占比

这一块目前来说还是非常方便的,直接使用android studio提供的apk分析工具,将自己的apk拉入进去,查看一下包体积的状况即可。

如上图所示,可以看到主要的体积占用是再lib下的so库, 资源文件和dex这三个部分。于是我们根据分析采取了以下措施。

实施瘦身

资源图片的压缩

对于资源图片,之前一直是收工进行压缩图片。我们对比了目前比较好的图片压缩工具。

  1. https://tinypng.com 在线的压缩工具,免费版对图片压缩有大小和size限制。但是实践过程中我们发现这个工具对某些图片压缩不成功时竟然出现了比原始图片大的状况。
  2. mac下的压缩工具ImageOptim, 此工具的压缩效果也是不错的,只不过这是一款软件,我们只能每次进行手工操作,集成度不高。那么有没有一款工具能做到效果好,同时可集成度高,不用我们每次进行手工操作的。
  3. pngquant https://pngquant.org 开源的图片压缩工具
    显而易见,我们最终选择了开源的pngquant工具,原因主要有两方面,一、开源,没有限制;二、便于集成。正是这两个方面的原因,我们团队中的小伙伴开发了一个gradle插件,可以对图片进行统一的压缩处理,并且可以使用此插件,依赖lint分析对代码中没有引用的资源文件进行移除。

    本地图片Webp化

    对于压缩过的图片,其实效果已经很好了,但是我们考虑还有没有再进行压缩的方案。于是,我们考虑了webp格式的图。对于webp格式的图片,虽然是google的技术,但是在android上使用还是有很大坑的。第一,系统版本的支持,4.0以上才支持webp格式; 第二, 不同版本的支持格式不一样, 4.1以上的系统才支持含透明的的webp图片。
    由于上边这些坑,我们不得不采取一些折中的方案。一般有两种选型,一、客户端增加开源库以便在低版本系统上支持webp图片;二、客户端只对不含透明度的图片进行webp化,避免4.1一下的手机无法解析的情况。
    综上所述,我们选择了第二种方案,只将不含透明度的图片进行webp化。

    Proguard规范化

    对应Proguard,之前客户端一致没有太注重这方面,导致proguard非常冗余。例如: 很多第三方库都是全部keep的,这就导致了很多无用的类也打包进入了dex文件导致体积的增加。
    因此,针对这种情况我们采用了case by case的排查。将proguard规范更加细致化。 比如,之前引入的google广告sdk,体积相当庞大,压缩前大约有1M,而proguard中我们直接全部keep的。
    1
    2
    3
    -keep class com.google.** {*;} 
    -keep class com.tencent.** {*;}
    -keep class com.sina.** {*;}

这种状况我们做了一下处理,分析对应的sdk包,只keep主sdk中没有混淆的类,sdk中已经做过混淆的类,我们不进行keep。这样就保证了我们调用的api是没有问题的,同时也去除了一些我们没有引用到的类。
注意: proguard的处理是非常有风险的,需要进行全面的测试

so库的处理

对so库的处理,我们做了两方面的工作。

I 移除除armeabi以外的所有so库

原因大家应该也都清楚。目前arm处理器的占比已经非常非常高。而且,调研一下你会发现微信,qq等装机量很大的apk都已经移除了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
buildTypes {
release {
minifyEnabled true
proguardFiles 'proguard.cfg'
signingConfig signingConfigs.release
ndk {
abiFilters "armeabi"
}
}
debug {
minifyEnabled false
signingConfig signingConfigs.release
ndk {
abiFilters "armeabi", "x86"
}
}
}

release状态下我们只保留了armeabi的so,但是debug状态下我们保留了armeabi和x86的so,原因是我们平时开发使用模拟器比较多,保留x86更便于我们平时开发

II 找出armeabi中体积很大的so,实行动态下发机制。

使用System.load(filePath)方法代替System.loadLibrary(libName)进行动态下发so。
当然这个要根据实际情况进行分析,首先要分析so是我们代码中自己进行load的还是sdk中进行load的。一般而言,自己进行load的so非常好处理,我们只需要动态下发,下发完成之后在进行load即可。而对于sdk内部load的so,就会稍微麻烦一点,我们不得不hook sdk内部load so的方法,修改其load的时机和路径。
幸运的是,我们遇到的体积最大的so,压缩前5M,是我们的一个播放器使用到的so,于是我们修改了播放器的load so的时机,下载完成之后进行load。 下载完成之前,如果有用户点击视屏,我们直接使用系统自带的MediaPlayer进行播放。

注意: System.load(filePath) 此方法的filePath有些手机是不支持sdcard下的文件的,因此动态下发so时,需要将so放到/data/data/{apkId}包下

夜间模式

夜间模式,新闻客户端这边一直使用的比较原始的方式,两套资源的方式。
于是我们希望你能做成动态下发夜间资源的形式。这项工作基本已经完成,但是由于种种原因目前还没有正式上线。

支持语言的精简

引用的很读标准库会支持很多国际语言(如v7等support包会含有很多语言),其实对于我们来说大多数是没有用的。其实只保留中文和英文基本上已经足够了。

Need TODO

dex瘦身

由于我们的热更新方案使用的是aop的方式,对字节码进行了修改,导致class文件均会增大,大约增大了1~1.5M,因此我们希望能找到更加合适的方案对字节进行修改,减小最后生成的dex体积。

support的拆分

google提供的最新的v4包已经对版本进行了拆分,因此我们希望以后只引入需要的support包,以便减小最终dex体积。


基于微博网友47Log 的补充:

资源混淆

其实资源混淆我们很早之前就已经做了,所以上边没有写。我们也是使用它的 AndRes,这个效果还是很明显的,网易新闻大小在24M 左右的时候,走完 AndRes 资源混淆,差不多能减掉1.5~2M。

使用 Vector 进行替换资源

这个也是可行的,因为 Vector 意味着体积会更小。如果时间允许的话,这个也是缩小体积的一个方面。不过从成本和效果上看不一定会是特别有效的方案。