首页 > 代码库 > android proguard使用心得和遇到的若干问题以及解决思路。

android proguard使用心得和遇到的若干问题以及解决思路。

今天决定对项目使用混淆工具proguard。于是便开始找proguard究竟放在sdk的什么地方,果不其然,我在sdk目录的tools文件夹中找到了proguard的文件夹。
配置方法这里简单的说明一下。
打开要混淆的android项目,找到project.properties文件。
后面添加上proguard.config=android.pro
现在我来解释说明一下这句话,后面的android.pro是proguard的配置文件,主要是告诉proguard应该怎么混淆文件。那么这个配置文件在哪里呢?
网上都是直接复制的,没有追其根源。我们可以在前面提到的proguard的文件夹里面,找到proguard-android.txt。这个是默认的混淆配置,已经针对android的代码做了优化了,但是并没有添加我们自己的混淆逻辑。

这个文件就是我后面的android.pro。我是这么做的,把该文件复制到项目中,然后修改名字为android.pro。
因此也就是告诉各位这个文件的名字没有硬性要求的,只要是正确的配置文件就可以了。

之后就开始立马签名文件了,然后自己反编译文件,发现里面都是abcd的类名,不由得很开心。

之后立马开始安装,果然就开始报错了。这里我们要分析错误的来源,才能正确开始添加我们自己编写的的配置代码到配置文件中。有些机子是直接就死机的,那么我们无法看到错误报告,有些是有系统自带report的报告,我们可以看到相关的堆栈错误。那么怎么解决直接死机崩溃,没有堆栈日志的情况呢?我们可以连接机子到电脑,在eclipse中看到设备,然后让它死机,那么我们同样可以再logcat中看到死机的原因。

这里要搞清楚,我们不是debug模式,因此报出的错误都是这样的

后面是unkonw source。因为我们没有关联相关的代码,因此是和开发模式是不同的,是看不到具体在第几行的。不过好在我们可以知道是什么方法的名字出现了异常。因此我们可以反编译混淆过的代码,查看和对比没有混淆的代码。发现问题所在,接下来我们才可以添加我们自己的混淆配置。接下来,我要举例说明遇到的问题,和解决的思路,供大家参考。

1.解决案例一:下面看一下部分的代码。

public abstract class Model {
	@Column(name = "Id")
	private Long mId = null;
//下面这个函数是另一个类里面的
private Field getIdField(Class<?> type) {
		if (type.equals(Model.class)) {
			try {
				return type.getDeclaredField("mId");
			}
			catch (NoSuchFieldException e) {
				Log.e("Impossible!", e);
			}
		}
		else if (type.getSuperclass() != null) {
			return getIdField(type.getSuperclass());
		}

		return null;
	}

如果我们直接的混淆的话,那么mId肯定会变成了abcd之类的词汇。
而在下面的方法中出现了反射方法。

type.getDeclaredField("mId");

那么我们在混淆之后肯定会报错的。并且返回一个null,笔者当时就是报了个空指针异常,程序的主要作用是查找指定类,发现它的id,并把具有该性质的类放入集合中。
很好,我们发现了这个错误。下面我们就开始添加如下的配置:
-keepclassmembernames class *******************.Model{
java.lang.Long mId;
}
上面的一连串*号是我的包名。
笔者一开始是这么写的。
-keepclassmembernames class *******************.Model{
Long mId;
}
后来查看了proguard文件夹下面examples文件夹下面的许多pro文件,发现了他们的共性,发现不是基本类型是不能这么写的。

后来在混淆之后,在项目中发现了这么个文件夹proguard。这个文件夹是混淆过程中产生的相关的日志文件。
大家可以再mapping.txt文件中找到proguard究竟给你混淆了什么,同时保留了什么。我对上面的Model类尝试了搜索,看看mId是不是混淆了,还是说保留了。

***********.Model -> com.a.d:
    java.lang.Long mId -> mId

结果搜索到这些语句
可以看到Model类已经被混淆成com.a.d了。而我们希望保留的mId,也确实没有混淆了。

2.解决案例二:
由于笔者的项目使用了fastjson框架,发现在解析从服务器下来的数据的时候发生了异常。
大家都知道json数据里面的成员变量是有具体的含义的,比如{username:‘xiaoming‘,password:‘dddddddd‘};
而我自己写的model,里面对应的username肯定是混淆的。因此我们要解析json数据,肯定是不希望它被混淆的。
因此我们可以在配置文件中添加如下:
-keep class com.xxxx.json.model.**{
*;
}
把json包下的实体类全部保留。

上面描述了两个解决案例:更多的配置例子可以查看sdk的proguard文件夹下面的examples目录。

接下来解释几个配置参数加深大家对proguard的配置的理解

保留所有的本地native方法不被混淆。这点在我们做ndk开发的时候很重要。
-keepclasseswithmembernames class * {
    native <methods>;
}


保留所有的set和get开头的方法。
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}


# We want to keep methods in Activity that could be used in the XML attribute onClick

我们保留在activity中的方法参数是view的方法,这样的话,我们在xml里面编写onClick就不会被影响了。
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

接下来附上相关的proguard的配置解释,是笔者从网上搜罗的。

-dontwarn 缺省proguard 会检查每一个引用是否正确,但是第三方库里面往往有些不会用到的类,没有正确引用。如果不配置的话,系统就会报错。
-keep 指定的类和类成员被保留作为 入口 。
-keepclassmembers 指定的类成员被保留。

-keepclasseswithmembers 指定的类和类成员被保留,假如指定的类成员存在的话

保留选项 
-keep {Modifier} {class_specification}    保护指定的类文件和类的成员 

-keepclassmembers {modifier} {class_specification}    保护指定类的成员,如果此类受到保护他们会保护的更好

-keepclasseswithmembers {class_specification}    保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。 

-keepnames {class_specification}    保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除) 

-keepclassmembernames {class_specification}    保护指定的类的成员的名称(如果他们不会压缩步骤中删除) 

-keepclasseswithmembernames {class_specification}    保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后) 

-printseeds {filename}    列出类和类的成员-keep选项的清单,标准输出到给定的文件 

压缩 
-dontshrink    不压缩输入的类文件 

-printusage {filename} 

-whyareyoukeeping {class_specification}     

优化 
-dontoptimize    不优化输入的类文件 

-assumenosideeffects {class_specification}    优化时假设指定的方法,没有任何副作用 

-allowaccessmodification    优化时允许访问并修改有修饰符的类和类的成员 

混淆 
-dontobfuscate    不混淆输入的类文件 

-printmapping {filename} 

-applymapping {filename}    重用映射增加混淆 

-obfuscationdictionary {filename}    使用给定文件中的关键字作为要混淆方法的名称 

-overloadaggressively    混淆时应用侵入式重载 

-useuniqueclassmembernames    确定统一的混淆类的成员名称来增加混淆 

-flattenpackagehierarchy {package_name}    重新包装所有重命名的包并放在给定的单一包中 

-repackageclass {package_name}    重新包装所有重命名的类文件中放在给定的单一包中 

-dontusemixedcaseclassnames    混淆时不会产生形形色色的类名 

-keepattributes {attribute_name,...}    保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses. 

-renamesourcefileattribute {string}    设置源文件中给定的字符串常量

最后谢谢各位的阅读。



android proguard使用心得和遇到的若干问题以及解决思路。