Android开发实践:利用ProGuard进行代码混淆

由于Android的代码大都是Java代码,所以挺容易被反编译的,好在Android ADT为我们集成了混淆代码的工具,一来可以混淆我们的代码,让程序被反编译后基本看不懂,另外还能起到代码优化的作用。发布项目前,建议打开Android的代码混淆功能。

Android ADT主要通过ProGuard工具来提供代码混淆,网上也有挺多博客文章讲这个的,但感觉很多都介绍得太过于复杂,这里我就以问答的方式来更加简洁地介绍下ProGuard吧。

1. ProGuard是什么

ProGuard是一个工具,用来混淆和优化Java代码。

工作方式:移除无效的代码,将代码中的类名、函数名替换为晦涩难懂的名字。

注意,它只能混淆Java代码,Android工程中Native代码,资源文件(图片、xml),它是无法混淆的。

2. 如何开启ProGuard

修改Android工程根目录下的project.properties文件,把proguard.config=....这一行前面的注释“#”去掉。

这一行指定了系统默认的proguard配置文件,位于Android SDK/tools/proguard目录下。

当然,你也可以自己编写配置文件,但不建议这样做,因此系统默认的配置已经涵盖了许多通用的细节,如果你还有额外的配置,可以添加在 proguard-project.txt 文件中。

注意: 只有在生成release版本的apk时,混淆配置才会起作用,debug版本的apk不会进行混淆。

3. 哪些内容需要手动配置

系统默认的配置已经涵盖了大部分的内容,但是如果你的工程中有如下内容,则需要手动添加配置到proguard-project.txt文件中。

(1) 只在 AndroidManifest.xml 引用的类

(2) 通过JNI回调方式被调用的函数

(3) 运行时动态调用的函数或者成员变量

(4) 当然,如果你不确定哪些需要手动配置,可以以默认的配置生成程序,当运行中发现ClassNotFoundException异常时,即可找到哪个类不该被混淆。

4. 手动配置的规则

手动添加的配置,一般以“-keep”开头,常用的配置命令分别示例如下:

假设Android工程中有一个接口和一个类:

package com.ticktick.example;

public interface TestInterface {
    public void test();
}

public class Test {

    private String mTestString;
    private final int mMinValue;
    private final int mMaxValue;

    public Test( int min, int max){
        mMinValue = min;
        mMaxValue = max;
    }

    public int getMinValue() {
        return mMinValue;
    }

    public int getMaxValue() {
        return mMaxValue;
    }

    public void setTestString(String testStr ) {
        mTestString = testStr;
    }
}

(1) 不混淆某个类的构造函数

例如:不混淆Test类的构造函数:

-keepclassmembers class
com.ticktick.example.Test {
    public <init>(int,int);
}


(2) 不混淆某个包所有的类或指定的类

例如,不混淆package com.ticktick.example下的所有类/接口

-keep class com.ticktick.example.** { * ; }

例如,不混淆com.ticktick.example.Test类:

-keep class com.ticktick.example.Test { * ; }

如果希望不混淆某个接口,则把上述命令中的class替换为interface即可。

(3) 不混淆某个类的特定的函数

例如:不混淆com.ticktick.example.Test类的setTestString函数:

-keepclassmembers class
com.ticktick.example.Test {
    public void setTestString(java.lang.String);
}

(4) 不混淆某个类的子类,某个接口的实现

例如:不混淆com.ticktick.example.Test类的子类

-keep public class * extends com.ticktick.example.Test

例如:不混淆com.ticktick.example.TestInterface的实现

-keep class * implementscom.ticktick.example.TestInterface {
    public static final com.ticktick.example.TestInterface$Creator *;
}

(5) 添加第三方依赖包

例如:添加android-support-v4.jar依赖包

-libraryjars
libs/android-support-v4.jar
-dontwarn
android.support.v4.**{*;}
-keep class android.support.v4.**{*;}
-keep interface android.support.v4.**{*;}

注意: 需要添加dontwarn,因为默认情况下proguard会检查每一个引用是否正确,但是第三方库里往往有些不会用到的类,没有正确引用,所以如果不配置的话,系统会报错。

5. 混淆后的调试信息解析

当代码混淆之后,输出的Log信息也会带有混淆内容,比如函数名和类名会被替换为晦涩难懂的名字,而与代码中的不一致。

因此,ProGuard工具还提供了恢复混淆内容的工具和文件。

当你开启了ProGuard混淆后,每次生成release版的apk时,Andriod工程的根目录下会对应生成一个proguard文件夹,该文件夹下的mapping.txt文件记录了混淆后的名字与混淆前的名字的对应关系,通过该文件,我们反向得到恢复后的Log信息。

假设Log文件名为log.txt,则恢复混淆的命令为:

$retrace.sh -verbose mapping.txt log.txt

注1:retrace.sh命令位于 <sdk_root>/tools/proguard/目录下

注2:你需要保存每一个release版本的mapping.txt,因为每一次release的混淆结果和映射关系都不一样。

关于Android的代码混淆我就总结到这儿了,你也可以去ProGuard的官方网页上获取关于ProGuard更加详细的介绍,有任何疑问欢迎留言或者来信[email protected]交流。

Android开发实践:利用ProGuard进行代码混淆,布布扣,bubuko.com

时间: 05-20

Android开发实践:利用ProGuard进行代码混淆的相关文章

Android开发实践:WIFI连接功能的封装

在上一篇文章<Android开发实践:WIFI扫描功能的封装>介绍了如何利用Andriod的API实现WIFI的扫描,本文则重点讲述一下如何连接WIFI吧,在此,也给出一个封装WIFI连接过程的类,提供简单的接口以供在各个代码工程中复用. 与WIFI扫描类似,WIFI的连接同样是一个耗时的过程,所以需要放到线程中执行,通过回调来通知调用者连接结果.该回调接口的定义如下: public interface WifiConnectListener { public void OnWifiConne

Android开发实践:Android交叉编译工具链的使用

前面2篇文章分别介绍了Android NDK编译的命令行参数,以及如何在任意目录使用Android.mk来编译本地c/c++代码,Andriod.mk和ndk-build只不过是Android官方提供了一套封装过的Android交叉编译环境而已,其实,你可以不用它,而直接通过传统的Makefile文件来编译你的c/c++代码的,本文即介绍如何直接通过传统的Makefile文件来编译可用于Android平台的库文件. 经常搞嵌入式开发的朋友对于交叉编译环境应该并不陌生,说白了,就是一组运行在x86

Android开发实践:为什么要继承onMeasure()

Android开发中偶尔会用到自定义View,一般情况下,自定义View都需要继承View类的onMeasure方法,那么,为什么要继承onMeasure()函数呢?什么情况下要继承onMeasure()?系统默认的onMeasure()函数行为是怎样的 ?本文就探究探究这些问题. 首先,我们写一个自定义View,直接调用系统默认的onMeasure函数,看看会是怎样的现象: package com.titcktick.customview; import android.content.Con

Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地

Android开发实践:以“专业”的态度处理多线程

刚开始学一门编程语言的时候,我总是会有一种困惑,怎样让自己的代码看起来更"专业"?很多时候,我们可以照着教材实现一些基本的功能,比如用Socket发送/接收几个字符,写一个线程完成某个异步任务,但是在实际的项目中,往往不那么简单,比如需要设计Socket通信协议,需要处理Socket的连接异常断开,需要考虑在线程阻塞的情况下如何正常退出和释放资源等等,关于这些"实战经验",前面的文章也有所涉及,以后有空准备再开个专题跟大家分享探讨一下,今天先简单地说说怎样更&quo

android开发异常信息收集程序代码

首先创建全局的Application ,此Application全局通用. package com.demo.utils; import com.demo.exception.CrashHandler; import android.app.Application; /** * 全局的context,任意位置调用 * @author Administrator * */ public class GlobalApplication extends Application { private st

Android开发实践:由new Handler()说开去

最近面试一些Android开发的应聘者,除了基本的Activity生命周期等基础问题以外,我一般还会问如下两个问题: (1) Service与Thread有什么区别? (2) 在Activity里new Handler()和在自己创建的Thread中new Handler()有什么区别? 第一个问题其实是一个伪命令,因为Service是Android四大组件之一,而Thread只是Java提供的一个封装了线程管理的工具类,无论是Activity还是Service,都可以通过Thread来创建一个

Android开发之利用SQLite进行数据存储

Android开发之利用SQLite进行数据存储 Android开发之利用SQLite进行数据存储 SQLite数据库简单介绍 Android中怎样使用SQLite 1 创建SQLiteOpenHelper对象并创建表 2 通过SQLiteDatabase对象运行增删改查操作 3 SQLiteDatabase之事务transaction 1.SQLite数据库简单介绍 SQLite.是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包括在一个相对小的C库中.它是D.RichardHip

如何在Android开发中让你的代码更有效率

如何在Android开发中让你的代码更有效率 最近看了一个视频,名字叫做Doing More With Less: Being a Good Android Citizen,主要是讲如何用少少的几句代码来改善Android App的性能.在这个视频里面,演讲者以一个图片app为例讲解如何应用Android中现有的东西来改善app性能问题. 这个图片app的代码:https://github.com/penkzhou/iogallery.ppt:http://greenrobot.qiniudn.