Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。
修改项目下project.properties文件,将下面代码前面的注释(#)去掉。
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
修改Module下build.gradle,将minifyEnabled
设置为true
。
public class MainActivity extends Activity {
private String mName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setName();
}
private void setName() {
mName = "董永康";
}
private String getName(){
return mName;
}
}
这里以ES为例。打开proguard.project.txt。原文如下。
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
大概阅读下。注意下面这行说明:
# By default, the flags in this file are appended to flags specified in ${sdk.dir}/tools/proguard/proguard-android.txt
这里说明我们的proguard.project.txt文件会被追加到proguard-android.txt文件下。也就是说真正的混淆文件其实是proguard-android.txt,我们再配置的文件只是添加在proguard-android.txt文件后面。proguard-android.txt文件只是基础配置没多少内容,有兴趣的可以去看下。
现在我们先不对混淆做任何配置,只使用默认的混淆看看有什么结果。反编译apk查看代码步骤如下:
dex2jar反编译命令为:d2j-dex2jar classes.dex
使用jd-gui打开后的代码如下:
public class MainActivity extends Activity{
private String a;
private void a()
{
this.a = "董永康";
}
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130903064);
a();
}
}
可以看到属性mName变成了a,getName()
方法由于没有调用,自动被取出掉了。这就是代码混淆的好处。你想想,如果你反编译一套代码后看见变量名甚至类名都变成了a、b、c,你还有兴趣接着看下去吗?
代码混淆生成apk之后,项目下面会多出来一个proguard文件夹,下面分别解释proguard文件夹中四个文件的作用。
上面只是基础的混淆,远远不能满足实际需求。下面添加一个我们自己的混淆规则。在proguard-project.txt中添加如下代码:
-keep public class com.example.testproguard.MainActivity{
void setName();
}
乍一看不知道什么意思,对比下混淆后的代码
public class MainActivity extends Activity{
private String a;
private void setName()
{
this.a = "董永康";
}
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130903064);
setName();
}
}
对比可以发现,setName()
方法没有被混淆。这就是-keep
关键字的作用。保持某部分代码不会混淆。有读者可能会觉得本来就是混淆,这会怎么又不混淆了。默认的混淆规则是:除了声明不被混淆的代码,其余的都会被混淆!比如我们在WebView
中和JS交互的时候,需要提供一个接口给JS调用,如果混淆了方法名,JS就会找不到这个方法。
上面只是个示例,更多参见下文:
-optimizationpasses 5 # 指定代码的压缩级别
-dontusemixedcaseclassnames # 是否使用大小写混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方jar
-dontpreverify # 混淆时是否做预校验
-verbose # 混淆时是否记录日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法
-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 # 保持哪些类不被混淆
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
native <methods>;
}
-keepclasseswithmembers class * { # 保持自定义控件类不被混淆
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int); # 保持自定义控件类不被混淆
}
-keepclassmembers class * extends android.app.Activity { # 保持自定义控件类不被混淆
public void *(android.view.View);
}
-keepclassmembers enum * { # 保持枚举 enum 类不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆
public static final android.os.Parcelable$Creator *;
}
-keep class MyClass; # 保持自己定义的类不被混淆
代码几乎都被我们混淆了,我们怎么再去查看错误日志?对着a.b.c(),鬼知道这是什么方法!骚年,别急。Google当然会为我们考虑到这种情况。下面讲解怎么查看混淆后的代码。
执行命令前
执行命令后
可以看到我们发生错误的方法为initViews()
,而不是a()
;感谢耐心阅读到最后!
google:http://developer.android.com/tools/help/proguard.html