目录
摘要:
学习混淆的最终目的:希望运用到当前开发的Android项目中,那么Android项目需要添加的混淆规则包括哪些呢?在《Android开发之混淆基础教程》介绍了混淆指定包名、指定类名和指定方法的基础知识,《Android开发之混淆高级教程(一)》介绍了保留关键字-keep
之间的区别,本篇文章主要介绍第三方类库、注解、序列化对象、实体、自定义View、枚举、native方法、callback方法、JS交互方法等混淆规则的写法,最后总结成一份可以通用的Android项目混淆规则。
一、Android项目混淆规则分类
在学习了Android开发之混淆基础教程,知道怎么保留指定的包名、类名、方法名以及字段后,其实已经掌握了混淆的大部分内容。为了更好地运用、记忆混淆的内容,将混淆规则划分成如下几类:
1.1 混淆第三方类库
怎么混淆第三方类库,这是一个很多同学可能会问的问题,因为我们并不是类库的开发者,对于类库的代码、结构不熟悉,自然不知道该添加什么样的混淆规则,这是比较头疼的事情。对于我个人来说,能够添加混淆规则的第三方类库,尽量添加,混淆其中一个好处压缩apk的大小,减少内存的消耗。
一个优秀的开源项目,开发者可能会提供对应的混淆规则,所以,添加第三方类库的混淆规则,第一种方法:登录GitHub或开源库官网,查找开源库的混淆规则,直接复制到当前Android项目中即可,查看当前项目添加的第三方类库,如下图:
以com.squareup.okhttp3:okhttp:3.8.1
类库为例,在GitHub搜索开源库okhttp
,查看混淆规则说明,如下所示:
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
直接复制上述规则,到我们当前开发的Android项目中即可
如果第三方类库,开发者没有提供对应的混淆规则,哪该怎么办?以com.nineoldandroids:library:2.4.0
为例,在GitHub查看开源库,没有找到混淆规则的代码,如果比较熟悉开源库的源码,可以自己定义开源库的混淆规则,但如果你还没有阅读过源码,不知道怎么写混淆规则,建议可以保留开源库,不混淆包名下(二级、三级及其以下)的所有文件,如下图:
在当前Android项目中,添加如下规则:
-keepnames com.nineoldandroids.**{*;}
混淆第三方类库,第一种方法:复制开源库的混淆规则到项目中,第二种方法:保留开源库包名及其以下所有类、方法,第三种方法:自己定义开源库的混淆规则,前提是熟悉开源库源码
1.2 混淆注解相关代码
这里所说的注解,不包括第三方类库使用到注解,使用注解的第三方类库,可以参考混淆第三方类库的方法。
保留注解相关代码,需要使用关键字-keepattributes [attribute_filter]
,该关键字的作用:指定需要被保留的任一可选项属性,属性可以指定一项或多项,可以选择的属性attribute_filter
包括如下几种类型:
Exceptions
Signature
Deprecated
SourceFile
SourceDir
LineNumberTable
LocalVariableTable
LocalVariableTypeTable
Synthetic
EnclosingMethod
RuntimeVisibleAnnotations
RuntimeInvisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeInvisibleParameterAnnotations
AnnotationDefault
混淆注解的相关代码,需要在Android项目中,添加如下规则:
-keepattributes *Annotation*
1.3 混淆序列化对象(Serializable、Parcelable)
继承自Serializable
或Parcelable
接口的类创建的对象称为序列化对象,为什么需要保留序列化对象的某些字段、方法,需要深入了解序列化的特点。
序列化对象以字节的形式保留到本地或在网络中传输,再通过反序列化将获取的字节重新组装成对象,为了保证组装的成功
1.3.1 混淆实现Serializable接口的类
- 需要保留的字段包括:非
static
、非transient
、非private
修饰的字段 - 需要保留的方法包括:非
private
修饰的方法,writeObject()
方法、readObject()
方法、writeReplace()
方法、readResolve()
方法
混淆实现Serializable
接口的类,在当前Android项目中,添加如下规则:
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
1.3.2 混淆实现Parcelable接口的类
混淆实现Parcelable
接口的类,在当前Android项目中,添加如下规则:
-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}
1.4 混淆实体类
我刚开始学习Android的时候,容易把实体对象、序列化对象弄错,实体类和序列化类的一个重要区别是:前者没有实现Serializable
接口,无法持久化对象。
实体对象和序列化对象的相同特点:
- 使用
private
定义属性字段 - 提供
set
和get
方法设置、访问属性字段 - 提供空的构造方法
1.4.1 实体类
/**
* Created by https://www.teachcourse.cn on 2017/8/29.
*/
public class City {
private double latitude;
private double longitude;
private String cityName;
public City(double latitude, double longitude, String cityName) {
this.latitude = latitude;
this.longitude = longitude;
this.cityName = cityName;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public String getCityName() {
return cityName;
}
public static String from(){
return "Chinese";
}
}
1.4.2 序列化类
/**
* Created by https://www.teachcourse.cn on 2017/8/30.
*/
public class CityBean implements Serializable {
private double latitude;
private double longitude;
private String cityName;
public CityBean(double latitude, double longitude, String cityName) {
this.latitude = latitude;
this.longitude = longitude;
this.cityName = cityName;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public String getCityName() {
return cityName;
}
}
混淆实体类,在当前Android项目中添加如下规则:
-keep class cn.teachcourse.bean.** {
void set*(***);
void set*(int, ***);
boolean is*();
boolean is*(int);
*** get*();
*** get*(int);
}
PS:在你的Android项目中,需要将cn.teachcourse.bean
目录改为你存放实体的路径。
1.5 混淆自定义View
重写继承自View或ViewGroup的方法,实现需要的效果,为了保证自定义的View可以布局文件中正常引用,不运行混淆自定义View:构造方法、set
方法、get
方法等
混淆自定义View,在当前Android项目中添加如下规则:
-keep public 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*(...);
}
1.6 混淆枚举类
什么是枚举?什么怎样在实际项目中应用?如果你不是很熟悉,可以先花两分钟大略阅读《Android开发之枚举(Enum)在实际项目中的应用》
混淆枚举类,在当前Android项目中添加如下规则:
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
1.7 混淆native方法
一个native
关键字修饰的方法,称为native方法,一个native方法长得很像abstract方法:只有方法签名,没有方法体,与抽象方法不同的是:native方法是由非Java语言实现的,比如C/C++语言实现;abstract方法是由Java语言实现的,下面是一个包含native方法的例子:
public class NativeDemo {
/**
*native方法,不包含方法体
*/
native public int cal(int a,int b);
static
{
System.loadLibrary("test");
}
public static void main(String[] args) {
NativeDemo nd = new NativeDemo();
System.out.println(nd.cal(3,5));
}
}
1.7.1 native方法实现的过程:
- 编写Java程序,javac编译生成
.class
文件; - 用javah编译生成的class文件,生成
.h
文件; - 编写
.cpp
文件实现native方法,其中需要包含上述生成.h
文件(.h
文件包含了JDK自带的jni.h
文件); - 将
.cpp
文件变异成动态链接库.dll
文件; - 在Java中调用
System.loadLibrary()
方法或Runtime.loafLibrary()
方法加载动态链接库文件,实现在Java中调用这个native方法; - 运行Java文件:java
Djava.library.path=[dll存放的路径]。
混淆native方法,在当前Android项目中添加如下规则:
-keepclasseswithmembernames class * {
native <methods>;
}
1.8 混淆callback方法
什么是callback方法呢?简单地说小明想要计算1024+1024等于多少的填空题,但因为小明还没学过四位数的加法,于是借助计算器的帮助,最终,小明得出1024+1024的答案,完成填空题,代码如下:
public class Student
{
private String name = null;
public Student(String name)
{
this.name = name;
}
public void setName(String name)
{
this.name = name;
}
public void callHelp (int a, int b)
{
new SuperCalculator().add(a, b, this);
}
public void fillBlank(int a, int b, int result)
{
System.out.println(name + "借助计算器计算:" + a + " + " + b + " = " + result);
}
}
1.8.1 callback方法
public class SuperCalculator
{
public void add(int a, int b, Student xiaoming)
{
int result = a + b;
xiaoming.fillBlank(a, b, result);
}
}
类Student
持有SuperCalculator
的引用,在A类中调用B类的方法;同时SuperCalculator
也持有Student
的引用,B类将计算结果通过A类的方法,回调给A类,在上面的例子中,fillBlank()
就是一个callback方法。
1.8.2 不陌生的callback方法
有接口的地方,就用到callback方法,这样的例子有很多,比如:列表选择、手势拖拽、焦点改变、按钮点击,这里看一下按钮点击的回调方法,代码如下:
btn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view){
Button btn=(Button)view;
btn.setTextSize(25);
}
});
在上面点击按钮的例子中,我们想要保留callback方法setTextSieze()
,在当前Android项目中添加如下规则:
-keep class android.widget.Button {
public void setTextSize(...);
}
1.9 混淆JS交互方法
为什么需要JS交互方法?在需要与H5进行JS交互的界面,前端根据Android客户端传递的对象,调用相关的方法,比如:页面开始加载方法onPageStarted()
、页面加载结束方法onPageFinished()
以及页面重载方法shouldOverrideUrlLoading()
,上述方法来自android.webkit.WebViewClient
类,也就是说为了保证WebView
加载H5界面可以正确WebViewClient
的方法,在proguard-rules.pro
添加如下规则:
-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.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
除此之外,我们会自定义JS可以调用的方法,如下图:
我们在当前加载H5网页的activity中,定义isLogin()
方法,在点击H5按钮,兑换积分礼品,这时通过JS代码,在H5界面调用Java代码中的isLogin()
,判断用户是否登录,我们需要保留所有JS会调用的Java代码,在proguard-rules.pro
添加如下规则:
-keepclassmembers class cn.teachcourse.LoginInterface {
public *;
}
PS:LoginInterface
是定义的一个接口,声明了H5界面会调用的方法,在你的Android项目中,需要修改成你自己的类。
二、总结
本篇文章通过例子的方式说明:第三方类库、注解、序列化对象、实体、自定义View、枚举、native方法、callback方法、JS交互方法规则的写法,当然在Android项目中,需要混淆的内容还不止这些分类,在熟悉了混淆基础知识的前提下,想要保留任意的包名、类名、方法名以及字段,都不是问题,将上面的规则总结如下:
# -----------------1.1 混淆第三方类库------------------------------------------
# 添加okhttp混淆规则
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
#添加nineoldandroids混淆规则
-keepnames com.nineoldandroids.**{*;}
# -----------------1.2 混淆注解相关代码------------------------------------------
-keepattributes *Annotation*
# -----------------1.3 混淆序列化对象------------------------------------------
# 混淆实现Serializable的类
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#混淆实现Parcelable的类
-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}
# -----------------1.4 混淆实体类------------------------------------------
# 注意将cn.teachcourse.bean更改为您项目存放实体类的路径
-keep class cn.teachcourse.bean.** {
void set*(***);
void set*(int, ***);
boolean is*();
boolean is*(int);
*** get*();
*** get*(int);
}
# -----------------1.5 混淆自定义View------------------------------------------
-keep public 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*(...);
}
# -----------------1.6 混淆枚举------------------------------------------
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# -----------------1.7 混淆native方法------------------------------------------
-keepclasseswithmembernames class * {
native <methods>;
}
# -----------------1.8 混淆callback方法------------------------------------------
-keep class android.widget.Button {
public void setTextSize(...);
}
# -----------------1.8 混淆JS交互方法------------------------------------------
-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.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
# 将cn.teachcourse.LoginInterface替换成您定义的接口
-keepclassmembers class cn.teachcourse.LoginInterface {
public *;
}
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/2494.html ,谢谢支持!