文章目录
- 前言
- 一、什么是代码混淆
- 好处
- 坏处
- 二、开启混淆
- 1.修改build.gradle
- 2.修改混淆文件
- 常用混淆命令
- 类修饰常用规则
- 例子
- 3.生成混淆结果文件
- 4.混淆代码模板
- 三、混淆注意事项
前言
本篇记录笔者对Android开发中代码混淆的认知
一、什么是代码混淆
关于代码混淆的定义,这里笔者选择自己认为讲的相对完整的话进行呈现
Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。
混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。
混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar文件体积大约能减少25%
好处
根据以上定义,可以简单对混淆的好处和坏处总结如下
- 防止反编译获取项目源码
- 精简编译后的apk文件大小
坏处
混淆的坏处如下
- 会延长编译时间,所以切记请勿在debug模式下开启混淆
- 混淆会让代码失去可读性,且无法还原
二、开启混淆
1.修改build.gradle
将minifyEnabled的值改为true即开启:其中debug为测试版本,release为发布版本
2.修改混淆文件
开启混淆后Android会用自带的混淆进行代码混淆,如在安装Android SDK的目录下的proguard-android.txt或proguard-android-optimize.txt文件为默认混淆文件。对于自己定义的混淆需要在proguard-rules.pro文件中进行配置:
常用混淆命令
类修饰常用规则
- 类:需要使用完全限定名;
- *:通配符,任意字符串,不包含包名分隔符(.);
- **:通配符,任意字符串,包含包名分隔符(.);
- extends:继承某类的类;
- implement:实现某接口的类;
- $:内部类;
- :所有构造方法;
- :所有成员变量;
- :所有方法;
- …:任意参数;
- 修饰符:public private protected
例子
3.生成混淆结果文件
因为我们提供给别人的一般都是release版本的库,所以要在build.gradle中开启release版代码混淆,配置好混淆规则后在Android studio的Terminal界面输入:
gradlew assembleRelease 再回车进行编译打包,如果编译失败则检查配置的混淆规则。成功后即可得到混淆后的aar或apk文件:
4.混淆代码模板
将报错的或者自己不需要的删除即可,然后在最后添加自己不需要混淆的代码:
#---------这里提供一份这个lib中最好不要混淆的地方,前边的配置都差不多,主要是第三方包以及其他不需要混淆的代码----
#---------------------------------基本指令以及一些固定不混淆的代码--开始--------------------------------
#<基本指令>
-optimizationpasses 5
-dontskipnonpubliclibraryclassmembers
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
#忽略警告
-ignorewarning
#记录生成的日志数据,gradle build时在本项目根目录输出apk 包内所有 class 的内部结构
-dump class_files.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从 apk 中删除的代码
-printusage unused.txt
#混淆前后的映射
-printmapping mapping.txt
#</基本指令>
#<基础>
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.support.multidex.MultiDexApplication
-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.** {*;}
#</基础>
#<view相关>
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * {
public void *(android.view.View);
}
#</view相关>
#<Serializable、Parcelable>
-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 public class * implements java.io.Serializable {*;}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#</Serializable、Parcelable>
#<R文件>
-keep class **.R$* {
*;
}
#</R文件>
#<enum>
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#</enum>
#<natvie>
-keepclasseswithmembernames class * {
native <methods>;
}
#</natvie>
#---------------------------------基本指令以及一些固定不混淆的代码--结束-----------
#---------------------------------第三方包--开始-------------------------------
#<okhttp3.x>
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
#</okhttp3.x>
#<retrofit2.x>
-dontnote retrofit2.Platform
-dontwarn retrofit2.Platform$Java8
-keepattributes Signature
-keepattributes Exceptions
-dontwarn okio.**
#</retrofit2.x>
#</okhttp3.x>
#<ButterKnife 7.0 以上>
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
#</ButterKnife 7.0 以上>
#<eventbus 3.0>
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
#</eventbus 3.0>
#<Gson>
-keep class com.google.gson.** {*;}
-keep class com.google.**{*;}
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
#</Gson>
#<glide>
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#</glide>
#<Rxjava RxAndroid>
-dontwarn 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;
}
#</Rxjava RxAndroid>
#----------------------------------第三方包--结束--------------------------
#---------------------------------一些不要混淆的代码--开始-------------------
-keep class net.arvin.afbaselibrary.nets.** { *; }
-keep class net.arvin.afbaselibrary.data.** { *; }
#<反射>
-keep class net.arvin.afbaselibrary.nets.BaseNet{*;}
#</反射>
#<js>
#</js>
#<自定义View的类>
-keep class net.arvin.afbaselibrary.ui.views.** {*;}
#</自定义View的类>
#---------------------------------一些不要混淆的代码--结束-------------------
三、混淆注意事项
- 混淆时对外暴露的接口层不能混淆,实体类不能混淆。