关于proguard的使用总结

在聊proguard使用之前,先说说proguard到底是什么东东,我主要做android开发,平时一般都听过android混淆打包的说法,直观的感觉就是把写好的java代码,通过一种编码方式给混淆了,让别人不容易看出代码逻辑以及java类之间的关系。其实,殊不知,这种混淆打包所依仗的就是这里要讲的progurad工具。

progurad工具实际上有四个功能。

压缩(Shrink):检测并移除代码中无用的类、字段、方法和特性(Attribute)。

优化(Optimize):对字节码进行优化,移除无用的指令。

混淆(Obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名。

预检(Preveirfy):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。

但是在android混淆打包中,我们实际上只需要使用Obfusacate这个混淆功能。

我们是通过配置命令的方式来使用proguard的,而命令又需要一些参数什么的,这个就涉及到通配符的问题,这里就从命令、通配符以及proguard不能混淆三个部分来对proguard进行总结。

1、proguard命令

根据我的个人经验,proguard使用最多的命令就是keep以及dontwarn这两个命令一个,一个表示保留对应的类方法属性等不要混淆,另一个就是表示不对指定的类、包中的不完整的引用发出警告。关于proguard的命令真的很多,遇到不懂得可以到ProGuard 最全混淆规则说明这篇文章中去查找,或者查看proguard manual

我们知道android中混淆打包需要进行配置

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

这里有一个proguard-android.txt是基础配置,还有一个proguard-rules.pro这个文件中开发这可以添加自己的一些配置。我们就以proguard-android.txt文件中的配置来简单说说proguard命令吧。

-dontusemixedcaseclassnames      #混淆时不生成大小写混合的类名,windows系统不能识别大小写

-dontskipnonpubliclibraryclasses #不忽略指定jars中的非public calsses

-verbose          #混淆过程中打印详细信息,如果异常终止则打印整个堆栈信息

-dontoptimize     #关闭优化

-dontpreverify    #关闭预验证,加快编译速度

-keepattributes *Annotation* #指定要保留的任何可选属性,可以指定多个,用逗号分隔符分割

-keep public class com.google.vending.licensing.ILicensingService   #指定对象不被混淆
-keep public class com.android.vending.licensing.ILicensingService

#指定保留的类和类成员,这里是指保留带native方法的类不被混淆
-keepclasseswithmembernames class * { 
    native <methods>;
}

#指定需要保留的类成员:变量或者方法,这里保留自定义控件的set/get方法不被混淆
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}
-keepclassmembers class **.R$* {
    public static <fields>;
}

#不对指定的类、包中的不完整的引用发出警告
-dontwarn android.support.**

#指定对象不被混淆
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}

#指定保留的类和类成员,这里指带Keep annotation的类不被混淆
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

基础配置已经把常用的proguard命令都用上了,代码中也给出了解释,还有不懂得,请查看上面提到的两篇文章。

2、proguard的关键字和通配符

肯定有同学看了上面的配置后还是有疑惑,proguard命令知道了,那么第一小节代码中的*、…这些符号是啥啊,这些就是这一小节要讲的通配符了。

我们要指定混淆规则,但是项目中的类啊,方法什么的太多了,我们不能一个个去指定,那么通配符便大有用武之地了,通配符的作用便是通过符号统一指定一种类型的类、方法以及属性等的特殊符号。

那么关键字是什么呢,这个比较简单,就是配置代码中的class、interface、enum、extends、implements等字符。

class 关键字表示任何接口类、抽象类和普通类; interface 关键字表示只能是接口类; enum 关键字表示只能是枚举类。如果在 interface 和 enum 关键字前面加上感叹号(“ ! ”)分别表示不是接口类的类和不是枚举类的类,而 class 关键字前面不能加感叹号。

extends表示存在继承关系,而implements表示存在实现关系。

下面来分类说说通配符,proguard的“?”以及“*”应该是可以通用的,可以分别表示一个字符和n个字符(不含”.”)。但是对于类,方法、属性、以及参数、返回类型、参数类型等的通配符还是有一定差异的,下面直接给出作为参考。

类名

1) ? :问好代表一个任意字符,但不能是句号(“ . ”,因为句号是包名分隔符);
2) * :单个星号代表任意个任意字符,但不能代表句号”.”
3) ** :两个星号代表任意个任意字符,且能代表句号

成员变量

1)可以通过变量类型 fieldtype 和变量名 fieldname 来精确指定
2)可以通过 表示类中的任何成员变量
3)星号(“ * ”)可以匹配类中的任何成员变量和函数

成员函数

1)可以通过返回类型 returntype 、方法名 methodname 和参数类型 argumenttype 来唯一限定,
2) 可以通过 来表示类中的任何成员函数,
3)星号(“ * ”)可以匹配类中的任何成员函数

构造函数

1)可以用 加上构造函数的参数来指定。

成员函数名、成员变量名

1)问号(“ ? ”)可以匹配一个任意字符
2)星号(“ * ”)可以匹配任意多个任意字符

成员变量类型、成员函数返回类型以及参数、构造函数参数类型

1) % :匹配任何原始类型,如 boolean 、 int 等,但不包括 void ;

2) ? :匹配一个任意字符,不包括句号;

3) * :匹配任意个任意字符,不包括句号;

4) ** :匹配任意个任意字符,包括句号;

5) * :匹配任意类型,包括原始类型和非原始类型,数组类型和非数组类型;

6) … :匹配任何数目个任何类型的参数。

另外还需要说明的是,在类名前、类中成员变量和成员函数名前,可以加上访问限定符(如 public 、 private 、 protected 等,修饰类、成员变量和成员函数的访问限定符各不相同),如果加上了访问限定符后,就表示要匹配的类、成员变量或成员函数的定义中必须包含这些限定符。

3、android中不能混淆的代码

proguard混淆是把混淆对象的名字进行修改,而有些对象的名字不能被修改,否则会报错,比如Activity类,修改后AndroidManifest中就找不到对应的Activityl了肯定要报错,还有反射也是。那么这里便总结下android开发中不能被混淆的代码

1) Android系统组件

即在AndroidManifest文件中注册的组件,如Activity,Service,BroadcastReceiver,ContentProvider,Application等。这些组件通过字符串的形式告知系统,当被调用的时候,如果这些类名被改变了,那么系统就会找不到它们。所以一般组件不进行混淆。

2)Resource文件的使用

例如各种资源的名字在xml已经固定使用,如果混淆也将找不到。所以一般R文件不混淆。

3)序列化的类

包括Parcelable和Serializable。如果混淆了可能UID等会错误,而且在反序列化的时候会找不到对应的名字。

4)自定义控件

如果自定义控件在xml中使用了,那么也不能混淆,否则找不到,当然如果在代码中用的话应该没问题。

5)本地方法

不能修改本地方法名,否则和so文件对应的方法就无法找到了。

6.枚举

系统需要处理枚举的固定方法。

7)用到反射的地方

因为反射需要通过名字去找变量和方法名,所以混淆后可能找不到。例如实际情况中的接口返回数据对应的实体类等等。

8)ILicensingService系列

ILicensingService系列(好像是aidl用的)和BackupAgentHelper备份相关的,不能进行混淆,不过这个东西一般没人用,所以不必在意。

9)annotations 注释

一般注释混淆其实可以混淆,但是混淆常会和反射相关联,所以还是不要混淆的好。

10)android.preference.Preference

此Preference可不是SharedPrefrences(但是有点关联),这个Preference也是一个组件,所以不能混淆。

11)第三方包一般不混淆

首先,第三方包没必要混淆,既然是第三方的,那么一般都是公开的,也就没有必要保护。其次,第三方包可能自己已经混淆过了,不用我们动手了。最后,第三方包里面可能用到了上述的这些不能混淆的点,如果我们不了解它,擅自去混淆,可能导致不能正常运行的结果。

12)WebView中Java和Javascript互调的方法不能混淆

因为它们都是通过名字去互相调用,如果名字变了就会找不到对应的方法。

13)某些内部类的使用和泛型的使用需要避免混淆

到这里proguard知识,我想总结的就算是总结完了,可能看了这些都不能立即写出很好的proguard配置来,但是了解本博文知识点后会对怎么配置成竹在胸,也能知道什么对象是不能混淆的。关于proguard的配置,最好的还是在实际开发中多尝试,找感觉。

4、参考文献

本文的内容很多都来自于其它博客,都是稍作修改,感谢那些童鞋们的辛苦付出,把参考文献列在这里吧,也方便自己查看。

1、ProGuard 最全混淆规则说明

2、Android ProGuard混淆基本语法(和通用配置)

3、Android中混淆技术总结

4、ProGuard代码混淆技术详解

5、Android Studio(十一):代码混淆及打包apk

6、Android混淆打包那些事儿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值