-
如果逆向修改了APK包中的文件,那么被修改的文件的摘要和MANIFEST.MF中的信息则不对应
-
如果修改了某个文件,则必须修改MANIFEST.MF中对应的摘要值,必须保证对应关系
-
要修改MANIFEST.MF的摘要值,会产生新的MANIFEST.MF,必然和CERT.SF中的记录不匹配
-
CERT.SF中记录了MANIFEST.MF整个文件的编码和其所有内容的编码值,逆向时必须修改CERT.SF
-
修改了CERT.SF后,安装apk时CERT.RSA文件中的内容和修改后的CERT.SF会不匹配,出现安装失败
-
逆向者只有拿到了开发者的秘钥才能完全创造一个相同的apk
优化建议:通过分析得出,除了RSA没有缩减机会外,其余两个文件都可以通过混淆资源名称的方式进行压缩。
优化Res目录
-
打包时剔除无用资源:
shrinkResources true
shrinkResources意思是收缩资源,将它设置为true,每次打包时就会自动排除无用的资源,不仅作用于图片,还会清理无用的layout资源等,但是只有配合开启混淆才能生效。 -
删除无用的语言:大部分app其实不需要支持几十种语言,国内应用,可以只支持中文
defaultConfig {
…
resConfig “zh”
}
这样配置后,打包时会排除私有项目、Android support库、三方库中的非中文资源文件。
- 控制raw中的资源大小:Raw和Assets可以用来存放资源,但两者有以下差异:
-
Assets目录允许下面有多级子目录,而Raw不允许存在字目录结构。
-
Assets目录不会产生R文件,Raw则相反
-
因为Raw文件会产生R文件的映射,所以可以被lint分析,而Assets不能
-
Raw不支持子目录让其无法成为存放多种类文件的目录
Raw虽然不会对文件大小有限制,但是存放的音频文件尽量不要使用无损格式,可以考虑同等质量但文件更小的音频格式,如OGG、wav、mp3等格式
减少layout文件
减少layout有两个方法:复用和融合。
复用:把一些页面共用的布局抽出来,这对于layout文件的管理还是瘦身都非常有用。
融合:对于不会被复用的layout呢?
-
shatter是一个类似于Fragment的解耦库,可以为同一个layout中不同区域的view进行逻辑解耦,还可以尽可能少的建立layout文件。
-
将没有必要复用的header头布局和ListView/RecycleView放在同一个layout布局中,通过代码完成头部的添加
动态下载图片
贴纸、表情这类的文件是相当大的,对于这类图片资源,强烈建议通过在线的方式获取,虽然有一点的复杂度和出错率,但是投入产出比还是不错的。
分目录放置图片
不同分辨率的图片应该放在不同的目录中,如果放错了,对于app运行时的内存大小会有一定的影响。
如果把一个本来应该放在drawable-xxhdpi里面的图片放在了drawable文件夹中,会出现什么问题呢? 在xxhdpi设备上,图片会放大3倍,图片内存占用会变为原来的9倍。
因为不同分辨率的图片大小有差距,很多认为可以使用一套图片来做,不用多套图,借此达到瘦身的目的。但谷歌建议针对不同分辨率出不同的图片。 比较通用的做法是:
-
聊天表情就出一套图,放在hdpi中(因为此类图片对于清晰度要求不高)
-
纯色小icon用svg制作,用矢量图适配所有分辨率
-
对于背景图等大图,出一套放在hdpi或者xxhdpi中
-
logo等权重较大的图片可针对hdpi、xhdpi、xxhdpi做多套图
-
如果某些图在真机中展示异常,就用多套图适配
-
如果有特殊机型,可针对性的补图
优化图片资源
图片的优化,最重要的是知道选择什么样的图片格式。
-
如果是纯色的icon,推荐使用svg
-
如果是两种以上颜色的icon,推荐使用webp
-
如果webp无法达到效果,则选择用png
-
如果图片没有alpha通道,则考虑使用jpg
-
因为svg是通过xml描述的,所以可以享受到资源优化和代码压缩,通常可以压缩到1kb
-
对于无透明度的大图,可以换为jpg格式进行有损压缩
webp格式从4.0开始原生支持,知道4.2.1才支持显示含有透明度的webp,一般png转换为webp,大小可以减少一半
另外,在大型项目中,会引入汗多support库以及三方库,如果库中包含一些大图,并且不会用到的话,可以用1x1的同名透明图片替代,达到技能编译通过,又能缩小体积的目的。
针对动画,尤其是帧动画,一直是相当占用资源的,现在可以使用svg动画或者Airbnb公司的Lottie动画库实现,如果使用Lottie可以直接使用json文件描述动画,图片会减少很多。
优化dex
dex文件是class文件被编译后可供art虚拟机理解的文件格式,可以理解为java代码包。
利用lint分析无用代码
可以借助Inspect Code,对工程做静态代码检查。Lint是一个强大的工具,它能做的事情不限于检查无用资源和代码,它能检测丢失的属性、写错的单位、会引起内存溢出的代码等,当然Lint虽然强大,但也会带来一些缺点,就是生成的信息量过大,不适合快速定位无用的代码。除此之外,Lint会提示不要使用枚举方法,如果将枚举变为int,apk的大小也会缩小一些,没减少一个enum,可以减少大约1到4kb的大小。
删除R文件
Android中的R文件,除了styleable类型外,所有的字段都是int型变量或者常量,且在运行期间都不会改变。可以在编译时记录R中所有字段的名称以及对应值,然后利用ASM工具遍历所有class,将引用R字段的地方替换成对应的常量 ThinRPlugin插件可以方便地将R.xxx的地方替换为具体值,可以减少一部分dex大小。
在最外层的build.gradle中加入如下依赖:
classpath ‘com.mogujie.gradle:ThinRPlugin
:0.0.2’
在内层的gradle文件中加入如下代码:
apply plugin: ‘thinR’
thinR {
//为了不影响日常开发的编译速度,debug版本可以不用删除R
skipThinRDebug = true
}
启用ProGuard:
ProGuard是一款优秀的代码优化、混淆工具,适用于java和Android。关于ProGuard,最需要了解的是它的方法检测机制。它将产出的class作为输入,然后寻找代码中所有的调用点,计算出代码中可达的调用关系图,然后移出剩余的部分,将真正用到的方法变量保留下来进行优化,最终输出一个新的class。
上图简单描述了什么是可达和不可达的代码
buildTypes {
release {
minifyEnabled true
shrinkResources true //压缩资源
proguardFiles getDefaultProguardFile(‘proguard-android-optimize.txt’), ‘proguard-rules.pro’
}
}
虽然这种方式成果显著,但也要配合正确的ProGuard规则才能起作用。
这张图展示了原始apk和混淆后apk的大小差异
getDefaultProguardFile(‘proguard-android-optimize.txt’), ‘proguard-rules.pro’
}
}
虽然这种方式成果显著,但也要配合正确的ProGuard规则才能起作用。
这张图展示了原始apk和混淆后apk的大小差异