标签:
谨以此文,记我在公司实习时,所接到的第一个正式的、真正有意义的任务——将公司即将发布的APK进行代码混淆。
混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。
以下资料,来自网络:
实际的优化效果是依赖于你的代码和执行代码的虚拟机的。简单的虚拟机比有复杂JIT编译器的高级虚拟机更有效。无论如何,你的字节码会变得更小。
仍有一些明显需要优化的技术不被支持:
>使非final的常量字段成为内联。
>像get/set方法一样使其他方法成为内联。
>将常量表达式移到循环之外。
在Android 2.3以前,混淆Android代码只能手动添加proguard来实现代码混淆,非常不方便。而2.3以后,Google已经将这个工具加入到了SDK的工具集里。该工具的具体路径:SDK\tools\proguard。当创建一个新的Android工程时,在工程目录的根路径下,会出现一个proguard的配置文件proguard.cfg。也就是说,我们可以通过简单的配置,在我们的elipse工程中直接使用ProGuard混淆Android工程。
在工程的根路径下,找到”project-properties.txt”文件,源码如下:
# This file is automaticallygenerated by Android Tools.
# Do not modify this file -- YOURCHANGES WILL BE ERASED!
#
# This file must be checked inVersion Control Systems.
#
# To customize properties used bythe Ant build system edit
# "ant.properties", andoverride values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink andobfuscate your code,uncomment this (available properties: sdk.dir,user.home):
# proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-22
你只要将蓝色那段代码,前面那个“#”号去掉,就能启动“ProGuard”工具,当然蓝色不是这个文件里面这段代码原有颜色,是我在文章里面为了说明加上的。现在进行导包操作,在导包的过程当中,“Eclipse”会自动使用“ProGuard”工具。另外需要说明的一点是,你直接“run”到设备的应用是没有经过混淆的,即便你已经启动了“ProGuard”工具,只有手动导包之后所获得的APK,才是经过混淆工具处理过的。在“proguard-project.txt”文件里面编写混淆规则,其实只有一个目的:如果某个类混淆后,将会导致应用无法正常运行(或者部分功能无法正常运行)那么就要在配置文件里,声明不要混淆这些类。我不关心为什么这些类混淆之后会出错,只关心哪些类在混淆后出错,然后在文件里面声明不要去混淆这些类。当然如果你非得要刨根问底,你也可以在网上找更详细地资料。
以下,是通用的,几乎所有工程都要避开的类(所谓避开,就是声明这些类不要被混淆):
>四大组件以及系统基本的API不要混淆。
语法规则:-keep public class * extends xxxx
代码示例:
#所有“Activity”及其子类不要混淆,同理,所有“Service”、“BroadcastReceiver”等等系统级别的类,不要混淆。 -keep public class * extendsandroid.app.Activity -keep public class * extendsandroid.app.Application -keep public class * extendsandroid.app.Service -keep public class * extendsandroid.content.BroadcastReceiver -keep public class * extendsandroid.content.ContentProvider -keep public class * extendsandroid.app.backup.BackupAgentHelper -keep public class * extendsandroid.preference.Preference -keep public classcom.android.vending.licensing.ILicensingService
-keepclasseswithmembernamesclass * { native<methods>; }
>保持自定义控件,以及指定格式构造方法不要混淆。
-keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet); #保持自定义控件类不被混淆,指定格式的构造方法不去混淆 } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet, int); }
-keepclassmembersclass * extends android.app.Activity { public void *(android.view.View); }
>保持自定义控件指定规则的方法不被混淆
-keeppublic class * extends android.view.View { public<init>(android.content.Context); public<init>(android.content.Context, android.util.AttributeSet); public<init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); }
>所有枚举类型不要混淆
-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
>需要序列化和反序列化的类不能被混淆(注:Java反射用到的类也不能被混淆)
#保持实现"Serializable"接口的类不被混淆 -keepnamesclass * implements java.io.Serializable #保护实现接口Serializable的类中,指定规则的类成员不被混淆 -keepclassmembersclass * implements java.io.Serializable { static final long serialVersionUID; private static finaljava.io.ObjectStreamField[] serialPersistentFields; !static !transient <fields>; private voidwriteObject(java.io.ObjectOutputStream); private voidreadObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
>保持实现"Parcelable"接口的类不被混淆
-keepclass * implements android.os.Parcelable { public static finalandroid.os.Parcelable$Creator *; }
>所有泛型不能混淆
-keepattributes Signature
>假如项目中有用到注解,应加入这行配置
-keepattributes *Annotation*
>保持R文件不被混淆,否则,你的反射是获取不到资源id的
-keep class **.R$*{*;}
>保护WebView对HTML页面的API不被混淆
-keep class **.Webview2JsInterface {*; }
>如果你的项目中用到了webview的复杂操作 ,最好加入
-keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap); public boolean *(android.webkit.WebView,java.lang.String); } -keepclassmembers class * extends android.webkit.WebChromeClient { public void *(android.webkit.WebView,java.lang.String); }至此,主要通用规则已经介绍完毕,你可以直接拷贝到“ProGuard”配置文件里面去,这些规则都通用的。
其实保持第三方库不被混淆并不复杂,最关键的就是要细心+耐心。为什么呢?保持第三方库不被混淆,是要将你所引用的所有第三方库,按照一定语法格式,写在混淆配置文件里面,简单地说,就是将你"Eclipse"工程里的"Android Private Libraries"目录下面所有的第三方的引用包,按照给定语法格式,全部(注意是全部)写到你的"proguard.project.txt"文件里面。来让我们看图说话。
>mapping.txt:表示混淆前后代码的对照表,这个文件非常重要。如果你的代码混淆后会产生bug的话,log提示中是混淆后的代码,希望定位到源代码的话就可以根据mapping.txt反推。每次发布都要保留它方便该版本出现问题时调出日志进行排查,它可以根据版本号或是发布时间命名来保存或是放进代码版本控制中。
>dump.txt:描述apk内所有class文件的内部结构。
>seeds.txt:列出了没有被混淆的类和成员。
>usage.txt:列出了源代码中被删除在apk中不存在的代码。
#保持所有适配器类不被混淆,本应用中,不加这个将会导致适配器类加载失败,所有列表项没办法显示 -keep public class * extends android.widget.BaseAdapter
标签:
原文地址:http://blog.csdn.net/u010707039/article/details/51718641