App瘦身和混淆
一、背景
在开发中我们经常遇到一个问题,那就是app包过大,有时候依赖一些第三方moudle 或者第三方sdk ,或者本身app 放了很多图片所导致app过大,那么怎么解决问题呢,不要急往下看。
二、需求
1.我们要利用混淆让我们的代码尽可能减少。
2.布局文件不要过多,太多了会乱,而且不方便整理.
3.图片不要太多。
4.对于无用资源,利用as能自动删除。
三、实现
分析app组成结构
首先利用2.2自带工具分析app,并有针对性的进行优化。
我们都知道apk是由:
asserts
lib
res
dex
META-INF
androidManifest
这几部分构成的
我们利用as分析工具,以我自己的新打包的工具进行讲述。
lib
lib目录下,会放各种so文件。利用分析工具去掉无用的so库
dex
dex文件是java代码打包后的字节码,一个dex文件最多只支持65535个方法,这也是我们为什么需要分包的原因。
因为dex 是分包不均的,可以理解为装箱,一个箱子的大小事固定的。但是你的代码量去不足。
res
res目录下存放很多资源,图片,字符串 、音频文件、各种xml文件等等。这是自己的的资源文件,没有做资源文件混淆,所以可读性很高。
例如微信的资源图片 都做了混淆所以都不可读。
以上就是就是分析工具 ,大家看到了我在说明结构时候很多,但是为什么,我只说这几点呢,其实很简单,例如asserts 目录下,我们会存放很多文件,但是这些文件就需要开发者手动的去优化了。
下面来说一说优化吧
res
res我们都熟知 ,我们图片文件 布局文件,还有一些配置文件都放在这。
1.布局优化
布局 优化其实不难,减少重复的布局文件提成一个xml 利用include 导入,这样很好的优化了页面,页面复用 ,我们有时候一个主页的其中一个模块和其他页面模块相同,同样的方法,这就是 include 的好处
2.图片优化
我们美工提供给我们png 图片 ,其实一个不大。但是都放在一起你在打包看看就很大,这样造成apk 超大。其实美工妹子也做了压缩的。
1.利用svg 替换少数图片。
2.利用压缩工具 减少png 图片大小,压缩有时候也会造成png图片失真。还有png 转换成webp 格式,可以用这个工具isparta
3.Lint工具可以检测项目中没有用到的资源文件,但是对于通过反射调用的图片无法过滤,需要我们手动排除
不要勾选id ,如果否选id 会影响打他databinding使用
其实去除无用资源还有一个 那就是在我们build.gradle里面配置
没错 就是这个 shrinkResources true ,
lib
lib 优化我们只需要搞定里面的so文件就差不多了,当然你如果引用了第三方jar ,需要注意奥。
so 文件优化 也需要在build.gradle里面配置
嘿嘿 没错就是他,设置支持的so库,去掉无用的so文件,同样还有另一种方法,就是根据手机cup支持的需要的库去引用 。那种方法我就不立出来了。
string.xml 说下这个
大部分应用其实并不需要支持几十种语言的,作为国内应用,我们可以只支持中文。推荐在项目的build.gradle中进行如下配置:
android {
//...
defaultConfig {
resConfigs "zh"
}
}
大概就是这样 ,下面我们来说一下混淆吧 。混淆是件麻烦的事。
前几天 手快 更新了一波as 到2.3 了,在之前没发现问题,更新到2.3 之后,我只能说一句哎呀我去。
在编写这里面时候先逐条介绍下基本指令区指令的含义
-optimizationpasses 5
代码混淆的压缩比例,值在0-7之间
-dontusemixedcaseclassnames
混淆后类名都为小写
-dontskipnonpubliclibraryclasses
指定不去忽略非公共的库的类
-dontskipnonpubliclibraryclassmembers
指定不去忽略非公共的库的类的成员
-dontpreverify
不做预校验的操作
-verbose
-printmapping proguardMapping.txt
生成原类名和混淆后的类名的映射文件
-optimizations !code/simplification/cast,!field/,!class/merging/
指定混淆是采用的算法
-keepattributes Annotation,InnerClasses
不混淆Annotation
-keepattributes Signature
不混淆泛型
-keepattributes SourceFile,LineNumberTable
抛出异常时保留代码行号
-keep class XXXX
保留类名不变,也就是类名不混淆,而类中的成员名不保证。当然也可以是继承XXX类的所有类名不混淆,具体代码不贴了,重在理解。
-keepclasseswithmembers class XXXX
保留类名和成员名。当然也可以是类中特定方法,代码不贴了,理由同上
首先开启混淆 在build.gradle中进行如下配置:
1.
minifyEnabled true 这句话就是开启混淆 true 为开启。
开启了之后sync new 一下
2.proguard-rules.pro 文件打开配置混淆指令
在这个混淆 文件中 有一些命令书不需要改变的
——————————————-基本不用动区域——————————————–
———————————基本指令区———————————-
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/,!class/merging/
-keepattributes Annotation,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
—————————————————————————-
———————————默认保留区———————————
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep class android.support.* {;}
# 保持native方法不被混淆
-keepclasseswithmembernames class * {
native ;
}
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
保持枚举enum类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep public class * extends android.view.View{
* get*();
void set*(*);
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
# 保持自定义控件不被混淆
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
保持Parcelable不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class *.R$ {
*;
}
-keepclassmembers class * {
void *(**On*Event);
}
—————————————————————————-
———————————webview————————————
-keepclassmembers class fqcn.of.javascript.interface.for.Webview {
public *;
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
—————————————————————————-
例如上面这些都是不需要动 的 同样 如果 你项目中
下面我把一下常用的第三方混淆立出来
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.*{;}
-dontwarn okio.**
Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.* { ; }
fastjson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.*{; }
rxjava
-dontwarn rx.**
-keep class rx.* { ; }
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.ArrayQueue*Field {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
butterknife
-keep class butterknife.* { ; }
-dontwarn butterknife.internal.**
-keep class *$$ViewBinder { ; }
-keepclasseswithmembernames class * {
@butterknife.* ;
}
-keepclasseswithmembernames class * {
@butterknife.* ;
}
eventBus
-keepattributes Annotation
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
(java.lang.Throwable);
}
glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser** {
**[]VALUES;
public *;
}
好了混淆就到这了 说一下为什么要混淆,首先我们看下面这个图片
这是为开启混淆状态下 我在很多地方都做了处理所以大概比混淆完的对了1-2m左右 ,再看混淆之后的
这样大概就有所比较了 这就是为什么要开启混淆的原因之一 ,开启混淆之后对反编译 也是一种压力 。具体的就不一一介绍了,网上反编译很多。
资源混淆方案 美团的 和微信的都不错 。我个人使用的是微信的地址也放在下面了
微信混淆方案
好了就写到这了,由于第一写博客 ,别取笑,谢谢大家看到这里。
QQ 群:547839514