这篇简单侃一侃Apk的一个瘦身,对于这个包的体积,不知道大家的公司是怎么看待的,反正我们公司,上到高层,下到开发,都是非常重视的,每次周例会,各个项目都要汇报,上个版本多大,预计这个版本,增加多少,优化多少,都是汇报的对象,所以啊老铁们,真的是和绩效挂钩,不是虚的。
Apk瘦身,做为一个Android开发者,这是多多少少都会接触到的,同样功能的App,200M和150M,给用户的第一直觉是不一样的,如果不是刚需,体积越大,用户的排斥也就越大,所以啊,铁子们,你以为瘦身,是简简单单的把体积变小,殊不知,直接影响着用户的真实体验,在开发中,是很有必要进行实施的,毕竟影响着网络数据流量和下载的等待时间。
如何针对性的做瘦身呢,不妨,我们先看一下,占用APK内存,都有哪些因素,大家可以找到apk包,双击,或者如下图进行选择一个apk打开。
打开后,如下图:
从上图中,哪些是比较占用内存的,我们一目了然,简单的分析下:
lib:
存放 aar或so 文件,针对so文件可能会有 armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips等很多不同cpu架构类型,随着时代的发展,大部分的应用市场也有了64位的安装包要求,所以以后的发展,估计只需要支持arm64-v8a这一个即可。
res
这个没什么好说的,存放编译后的资源文件,如, drawable、layout 等等。
assets
应用程序的资源,应用程序可以使用 AssetManager 来检索该资源,比如一些静态数据,图片资源可以放至此处。
META-INF
该文件夹一般存放于已经签名的 APK 中,它包含了 APK 中所有文件的签名摘要等信息。
classes.dex
classes 文件是 Java Class,被 DEX 编译后可供 Dalvik/ART 虚拟机所理解的文件格式
AndroidManifest.xml
Android 的清单文件,用于描述应用程序的名称、版本、所需权限、注册的四大组件
从以上的查看apk的内存占用大小,我们就可以有针对性的进行瘦身, 一般而言,无非就是两个方面,一个是资源入手,另一个就是代码入手。
资源入手
图片的使用
说到资源,就不得不提到图片,图片可以说是资源里占用内存最多的,那么如何合理的使用图片呢,首先就是解决多套图的问题,我们知道,在开发中,针对图片,有hdpi,xhdpi,xxhdpi,xxxhdpi等不同倍数的文件夹,如果让UI每个倍数都设计一套图,虽然在适配上满足了不同分辨率的展示,但无形当中,会增加数倍的体积;相对于当下的市场而言,低分辨率的手机越来越少,其实也不用准备很多套图,特别针对国内市场而言,一般xxhdpi就可以满足实际的需求了。
除了多套图的问题,还有就是图片格式的问题,大家可以发现,相对png,jpg而言,webp格式的图片在同分辨率下,要小25%以上,不仅可以提高加载速度,而且还节省内存,使用webp格式的图片,何乐而不为。
在实际的开发中,针对图片除了以上的措施,还有就是点9图的使用,特别是一些背景图片,我们就可以让UI设计一个特别小的图片,转换点9后再进行拉伸,这也是节约内存的一种方式。
如果项目中有很多体积比较大的图片,建议放到服务端,改本地加载为远程加载,如果没有这个条件,或者产品有规定必须要加载本地的,那么不妨我们可以走一步程序,那就是图片压缩,压缩后再进行加载;当然了压缩平台是有很多的,比如Tinypng。
layout的使用
关于layout,无非就是复用了,比如很多页面有着共同的布局,我们就可以单独的抽出来,使用include,在每个页面引用即可了,再比如,大部分的页面都是列表,其实layout文件也没必要多创建,使用一个共用的即可。
color,dimens,string,style等
对于这些资源文件,我们能复用就复用,一般有一套即可。
移除未使用资源
随着项目的迭代开发,可能有很多的历史资源,已经用不到了,针对这些文件,我们就可以进行剔除,当然了,有一些难以发掘的,我们可以在build.gradle文件里,开启shrinkResources
的属性,这个属性可以帮助我们移除那些在程序中使用不到的资源文件。
android { buildTypes { release { shrinkResourcestrue } } }
代码入手
so库使用
前边我们针对apk进行分析过,so绝对是占体积的一个大头,所以说,针对so,我们需要制定出一套合适的方案,既能减少体积,也能够平稳运行使用。我们都知道,cpu架构有很多类型,每支持一种,就要有对应的so文件,像目前我们公司开发的应用,增加一种,可能至少会增加三四十兆的体积,所以,这个是很恐怖的。
但还好,以后主流的大方向是64位, 也就是我们在应用中,只支持一种arm64_v8a即可,但是,主流是主流,目前还是不能支持一种,这个,在实际的开发中已经验证,如果只支持arm64_v8a,发现在部分手机如红米手机,就会找不到对应的so文件,就会发生崩溃,所以,还是要进行多cpu架构类型适配。
那么怎么解决这个问题呢?分开打包,也就是64位打64位的,32位打32位的,出不同的包进行上传,各个应用市场也都是支持的。唯一需要解决的就是,应用内的一个更新问题,这个就需要在多加一层判断,就是判断当前手机的cpu架构,然后更新下载对应的apk,这样就可以解决因so导致的体积增大问题。
当然了,以上的解决方式属于静态加载的解决,并没有根本上解决so占用内存的问题,如果想彻底的解决,那么就需要动态加载了,也就是本地不放置任何so库,全从服务端,进行下发,然后动态加载,这个网上也有很多案例,这里就不细说了。
代码复用
这个就无需多言了,抽取父类,抽取工具类等,大家都耳熟能详了,但还有一种情况,大家需要注意,那就是一个项目存在一种功能即可,比如网络,图片加载,数据缓存等,一个项目如果有多种的话,不能使用起来乱,不好管理,而且还会增加体积,想想看,一个项目,有Glide,Picasso还有Fresco,你的第一想法是什么?
减少 ENUM 的使用
每减少一个 ENUM 可以减少大约 1.0 到 1.4 KB 的大小。
WebView的使用
针对一些页面,可以用H5的话,就尽量用H5,H5不仅可以灵活更改,还可以大幅度减少apk的体积,毕竟当对于很多原生开发的页面,H5只需要一个webview容器。
开启混淆
在 build.gradle 文件相应的构建类型中添加 minifyEnabled true。
打开这些编译属性之后,程序在打包的时候就不会把没有引用到的代码编译进来,以此达到减少安装包大小的目的。
android { buildTypes { release { minifyEnabledtrueproguardFilesgetDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }
支持插件化
插件化开发和组件化不同,插件化开发就是将整个app拆分成很多模块,每个模块都是一个apk(组件化的每个模块是一个lib),最终打包的时候将宿主apk和插件apk分开打包,插件apk通过动态下发到宿主apk,进行分解apk大小。
以上呢,简单的总结了Apk瘦身的一些方式,其实瘦身的工作呢,尽量提前,也就是说,在项目的启动之时,就应该全方位的考虑,而不是等到日后的亡羊补牢。