在需要混淆的工程目录下(package/apps/下的工程)添加proguard.flags文件,该文件即为网络传说中的proguard.cfg,只是命名不一样而已,然后再Android.mk中添加如下两句:

LOCAL_PROGUARD_ENABLED := full
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
上面的full 也可以是custom,如果不写这句,那还得添加如下一句:
TARGET_BUILD_VARIANT := user或者TARGET_BUILD_VARIANT := userdebug

这样后在工程目录下执行mm便可以看到在out目录下生成了形如proguard.classes.jar的东东,这就说明已在编译中启动了proguard

但反编译一看,并未出现网络云说的abcd替代符号,其实代码并未真正混淆:

android在编译时默认关闭了混淆选项,有去研究build/core目录的同志会发现这里也有个proguard.flags文件,其实在proguard的过程中,编译器会调用包括本地目录下和系统定义了的多个proguard.flags文件,而在这个文件中混淆的选项被禁止了,故而编译出来的apk仍未混淆。因此将如下句子注释掉便可实现真正的混淆编译:

# Don't obfuscate. We only need dead code striping.
-dontobfuscate(将该句加个#号注释掉)
 
好奇的同志还可以继续看看,为什么TARGET_BUILD_VARIANT := user和LOCAL_PROGUARD_ENABLED := full二选一即可,详见build/core/package.mk:
 
LOCAL_PROGUARD_ENABLED:=$(strip $(LOCAL_PROGUARD_ENABLED))
ifndef LOCAL_PROGUARD_ENABLED
ifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),)
# turn on Proguard by default for user & userdebug build
LOCAL_PROGUARD_ENABLED :=full
endif
endif
ifeq ($(LOCAL_PROGUARD_ENABLED),disabled)
# the package explicitly request to disable proguard.
LOCAL_PROGUARD_ENABLED :=
endif
proguard_options_file :=
ifneq ($(LOCAL_PROGUARD_ENABLED),custom)
ifneq ($(all_resources),)
proguard_options_file := $(package_expected_intermediates_COMMON)/proguard_options
endif # all_resources
endif # !custom
LOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS)
 
3,如何书写proguard.flags文件
 
参数解释:
-include {filename}    从给定的文件中读取配置参数
-basedirectory {directoryname}    指定基础目录为以后相对的档案名称
-injars {class_path}    指定要处理的应用程序jar,war,ear和目录
-outjars {class_path}    指定处理完后要输出的jar,war,ear和目录的名称
-libraryjars {classpath}    指定要处理的应用程序jar,war,ear和目录所需要的程序库文件
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的库类。
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可见的库类的成员。
保留选项
-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}    设置源文件中给定的字符串常量
 
4,实例(SkyvideoPlayer)
-optimizationpasses 3
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-dontpreverify
-verbose
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version.  We know about them, and they are safe.
-dontwarn
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#by jiang
# -dontoptimize  #是否对类内部代码进行优化,默认优化
# Don't obfuscate. We only need dead code striping.
# -dontobfuscate  #不进行优化
#项目使用到的第三方jar包
-libraryjars ../libs/fastjson-1.1.23.jar
 
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature   #不优化泛型和反射
 
#android默认项
-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 com.android.vending.licensing.ILicensingService
 
# for skyworth fastjson
-keep class com.alibaba.fastjson.** { *; }
 
#以下为android默认项
-keepclasseswithmembernames class * {
    native ;
}
 
-keepclasseswithmembernames class * {
    public (android.content.Context, android.util.AttributeSet);
}
 
-keepclasseswithmembernames class * {
    public (android.content.Context, android.util.AttributeSet, int);
}
 
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
 
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

5,错误总结

 

问题一:使用gson包解析数据时,出现missing type parameter异常

程序中用到了gson的new typeToken,结果打包成apk发布时,发现抛出异常,但不通过打包apk时发现一切正常,百思不得其解,最初怀疑没有将gson-1.7.1.JAR打包进去,后来经过测试发现gson的其他方法经过打包也能正常运行,最后上网找了2天,终于在google gson论坛中找到了解决方法。

第一种:在 proguard.cfg中添加
-dontobfuscate
-dontoptimize
 
第二种:在 proguard.cfg中添加
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
 
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
 
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
这两种方法都测试可行,第一个方法没有混淆编译,第二个方法能够混淆编译
问题二:
反射类不能进行混淆编译,需加入
-keep class com.test.model.response.** {*;}
问题三:
android辅助jar包异常,在proguard.cfg中加入
-dontwarn android.support.v4.**
-keep class android.support.v4.** {*;}

问题四:(同1)

类型转换错误,因为我用的泛型,所以在调用某些方法的时候,会出现这种错误,后面在混淆配置文件加了一个过滤泛型的语句,如下。

-keepattributes Signature

过后,就没有出现类似的类型转换错误。

 

问题五:空指针异常,这个错误是我对比前面的错误来说,所用的时间比较短,开始是找不到方法到底是哪个(原因是上面提到的混淆后方法名相同),所以就把这个类里面的所有方法都过滤掉,这样我没用多少时间,也就找到了具体的方法,可还是不明白原因,后面发现了其中的一个if判断,我利用反射筛选方法,关键字是“get”,突然我就震精了,大叫一声——soga,原来我 model的 set/get方法名全部都被混淆了,所以筛选不到方法,返回的也就是null值,自然下面用到这个方法的返回值就会抛出空指针异常。

 

解决方法:把 model包下面的所有类,全部过滤掉。

 

总结:如要用到反射,反射一般就会利用到泛型,所以必须要把泛型的全部过滤掉,如果有根据变量名或者方法名判断的,记得所在的类需过滤掉,之中还有用到 annotation的地方,要加入一行代码,如下:

 

-keepattributes *Annotation*

这样就能过滤掉所有的annotation,否则也会抛出空指针异常。