Android 代码混淆的实践
开发 apk 的时候当然要考虑保护好自己的代码,Android 环境就提供了 ProGuard 来进行代码混淆,确实是一个非常有用的工具,但用起来也确实够折腾的。
1. 基本配置
eclipse 下建立 android 工程,就会生成 proguard.cfg 和 project.properties,在后面的文件追加 proguard.config=proguard.cfg 即可让前面的配置文件在 export 时生效。默认的那个文件有一些内容,这里给一个更通用点的。
##—————Begin: proguard configuration common for all Android apps ———-
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keepattributes *Annotation*
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
# 以下两个命令配合让类的路径给删除了
-allowaccessmodification
-repackageclasses ”
# 记录生成的日志数据,在 proguard 目录下
-dump class_files.txt
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
# 异常都可以忽略就打开
#-dontwarn
-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
-dontnote com.android.vending.licensing.ILicensingService
-keepnames class * implements java.io.Serializable
# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn’t save them.
-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 ();
}
# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
native ;
}
-keepclasseswithmembernames class * {
public (android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public (android.content.Context, android.util.AttributeSet, int);
}
# Preserve static fields of inner classes of R classes that might be accessed
# through introspection.
-keepclassmembers class **.R$* {
public static ;
}
# Preserve the special static methods that are required in all enumeration classes.
-keepclassmembers enum * {
public static **[] values ();
public static ** valueOf (java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 如果你的工程是对外提供方法调用就打开
#-keep public class * {
# public protected *;
#}
##—————End: proguard configuration common for all Android apps ———-
2. 解决 export 打包的报错
这个时候 export 提示“conversion to Dalvik format failed with error 1”错误,网上说法有好多种,最后我还是把 proguard 从4.4升级到4.8就解决了。官方地址是 http://proguard.sourceforge.net。上面的配置文件参数可以在这里查阅。
升级办法很简单,就是把 android sdk 目录下的 tool/proguard 目录覆盖一下即可。
3. 打包出来的程序如何调试
一旦打包出来,就不能用 eclipse 的 logcat 去看了,这里可以用 android sdk 中 ddms.bat 的 tool 来看,一用就发现和 logcat 其实还是一个东西,就是多了个设备的选择。
4. 使用 gson 需要的配置
当 Gson 用到了泛型就会有报错,这个真给郁闷了半天,提示“Missing type parameter”。最后找到一个资料给了一个解决办法,参考:http://stackoverflow.com/questions /8129040/proguard-missing-type-parameter。
另外我又用到了 JsonObject,提交的 Object 里面的 members 居然被改成了a。所以上面给的东西还不够,还要加上
# 用到自己拼接的 JsonObject
-keep class com.google.gson.JsonObject { *; }
我个人建议减少这些依赖包混淆带来的麻烦,干脆都全部保留不混淆。例如
-keep class com.badlogic.** { *; }
-keep class * implements com.badlogic.gdx.utils.Json*
-keep class com.google.** { *; }
5. 使用 libgdx 需要的配置
参考 http://code.google.com/p/libgdx-users/wiki/Ant
6. 验证打包效果
我是利用了 apktool 的反编译工具,把打包文件又解压了看了一下,如果包路径、类名、变量名、方法名这些变化和你期望一致,那就 OK 了。命令:
apktool.bat d xxx.apk destdir
总结
这个东西用起来也不是很简单,特别是你程序用到的高级特性多,就更容易出问题。另外 proguard 的参数看起来确实也有点不好理解,打包过程慢,测试也比较浪费时间。东西虽好,但真不是那么容易上手。