内部信息源与外部信息源搜索引擎优化方法的异同(重新定位和预先验证Java6的现有类文件(组图) )
优采云 发布时间: 2022-02-10 17:05内部信息源与外部信息源搜索引擎优化方法的异同(重新定位和预先验证Java6的现有类文件(组图)
)
@[目录]
了解 ProGuard
Android开发伙伴或多或少都接触过困惑,很多人对困惑非常困惑。当一个版本需要发布时,从互联网上加载一个混乱的文件,或者从其他项目中复制一份副本,修改它,然后不管它。有问题就会卡死,各种百度也不一定能解决问题。. 本文力求让大家熟悉混淆规则,快速上手。知道它也可以知道为什么。
从 Android Studio2.3 开始就集成了 ProGuard。ProGuard 是 Java 类文件的混淆器,它集成了压缩器、优化器、混淆器和预验证器。与其他 Java 混淆器相比,ProGuard 的主要优势可能是其紧凑的基于模板的配置。通常只需几个直观的命令行选项或一个简单的配置文件就足够了。ProGuard 减少了已处理代码的大小并带来了一些潜在的效率提升。处理几兆字节的程序和库只需要几秒钟。
ProGuard 的典型用途是:为更小的代码存档创建更紧凑的代码、更快的网络传输、更快的加载和更小的内存占用。使程序和库更难逆向工程。列出无效代码,可以从源代码中删除。重新定位和预先验证 Java 6 的现有类文件,以利用 Java 6 更快的类加载。
在 gradle 中配置 ProGuard 以启用混淆很简单:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
复制代码
minifyEnabled 属性设置为 true 以启用混淆。proguardFiles 属性指定混淆文件所在的目录。proguard-android.txt是sdk路径下默认的混淆文件,下面的‘proguard-rules.pro’是我们自定义的混淆文件。
ProGuard 处理代码如下: Shrunk 压缩器检测并删除未使用的类、字段、方法和属性。Optimize 优化器分析和优化方法的字节码。Obfuscate 混淆器用简短的无意义名称重命名剩余的类、字段和方法。这些步骤使代码库更小、更高效且更难逆向工程。最后的 Preverify 预验证器在类中添加预验证信息,这是 Java Micro Edition 提高 Java 6 启动时间所必需的。
ProGuard 详细说明什么是压缩?
Java 源代码(.java 文件)通常被编译成字节码(.class 文件)。字节码比 Java 源代码更紧凑,但字节码可能仍然收录许多未使用的代码,尤其是在收录库的情况下。ProGuard 等压缩器可以分析字节码并删除未使用的类、字段和方法。该程序在功能上保持等效,包括异常堆栈跟踪中给出的信息。
什么是混淆?
默认情况下,编译后的字节码仍然收录很多调试信息:源文件名、行号、字段名、方法名、参数名、变量名等。这些信息使得直接编译字节码和对整个程序进行逆向工程变得简单. 有时,这是不可取的。ProGuard 之类的混淆器可以去除调试信息并用无意义的字符序列替换所有名称,从而使逆向工程代码变得更加困难。同时它进一步压缩了代码。除了异常堆栈跟踪中给出的类名、方法名和行号之外,该程序在功能上保持相同。
反射
反射给代码的自动处理带来了特殊的问题。在 ProGuard 中,在代码中动态创建或调用的类或类成员必须指定为入口点,并使用 keep 选项进行保护。例如,Class.forName() 构造可以在运行时引用任何类。通常无法预见必须保留哪些类(及其原创名称),例如可以从配置文件中读取类名。因此,必须在 ProGuard 配置中使用相同的简单 -keep 选项指定它们。
此外,如果某些类或类成员需要保留,ProGuard 会提供一些建议。例如,ProGuard 会注意像“(SomeClass)Class.forName(variable).newInstance()”这样的结构。这些可能表明类或接口 SomeClass 或其实现可能需要保留。然后我们可以相应地调整混淆配置。
混淆选项
一个混淆文件主要由一系列保留选项和非保留选项组成。keep 选项用于告诉 ProGuard 哪些类和类成员不被混淆;non-keep 选项包括输入、压缩、优化、混淆、通用等选项,用于告诉 ProGuard 附加配置。
非保留选项
#Duplicate class a.a.a.a found in modules classes.jar (:libA-release:) and classes.jar (:libB-release:)
复制代码
这时候keeppackagenames可以指定一个库的包名不混淆以避免这个问题。
-keepattributes [attribute_filter] 指定要保留的可选属性。可以使用一个或多个 -keepattributes 指令指定属性。可选过滤器是一个以逗号分隔的属性名称列表。属性名称可以收录 ? , * 和 ** 通配符,或在它们前面加上 !。典型的可选属性包括:Exceptions、Signature、InnerClasses、Deprecated、SourceFile、SourceDir、LineNumberTable、LocalVariableTable、LocalVariableTypeTable、Synthetic、EnclosureMethod、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations 和 AnnotationDefault。
例如,在处理库时,至少应保留 Exceptions、InnerClasses 和 Signature 属性。还应保留 SourceFile 和 LineNumberTable 属性以生成有用的混淆堆栈跟踪。最后,如果您的代码依赖于注释,您可能希望保留它们。
例子:
-keepattributes Exceptions,InnerClasses,Signature #保留内部接口或内部类、内部类、泛型签名类型
-renamesourcefileattribute SourceFile #将崩溃日志文件来源重命名为“SourceFile”
-keepattributes SourceFile,LineNumberTable #产生有用的混淆堆栈跟踪
-keepattributes *Annotation* #保留注释
复制代码
此外,名称前面可以加上减号“!”。从匹配的文件名中排除该文件名。例如,
"!**.gif,images/** " # 匹配images目录中的所有文件,gif文件除外。
"!foobar,*bar" #匹配所有以bar结尾的名称,但foobar除外。
复制代码
保留选项
keep选项用来在混淆规则中声明需要保留的类和类成员,防止它们被删除和重命名。一般的格式如下:
复制代码
-keep 选项 class_specification class_specification 是类和成员的模板,用于指定应用保持规则的多个类及其成员。
根据是否可以在压缩阶段删除和在混淆阶段重命名,keep选项分为两类:第一类,没有名称,不能删除,不能重命名:-keep,-keepclassmembers,- keepclasses和members,分别对应保留类和类成员,只保留类成员,不指定类名,根据类成员查找所有满足条件的类,保留类名和成员名。第二类,有名字,不能重命名:-keepnames,-keepclassmembernames,-keepclasseswithmembernames,分别对应保持类和类成员不被重命名,只保持类成员不被重命名,根据类找到条件成员。所有未指定类名的类,保持类名和成员名不被重命名。对于第二个类,如果该类没有被调用,则会在compaction阶段被删除。
如图所示:
类规范
class_specification 是类和成员的模板,用于指定应用保持规则的多个类及其成员。格式如下:
[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
[extends|implements [@annotationtype] classname]
[{
[@annotationtype] [[!]public|private|protected|static|volatile|transient ...] |
(fieldtype fieldname);
[@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] |
(argumenttype,...) |
classname(argumenttype,...) |
(returntype methodname(argumenttype,...));
[@annotationtype] [[!]public|private|protected|static ... ] *;
...
}]
复制代码
解释:
1. 请注意,?,*和** 通配符永远与基本类型不匹配。
2. 此外,只有*** 通配符可以匹配任何维度的数组类型。
例如,"**get*()"匹配"java.lang.Object getObject()",但不匹配" float getFloat()",也不匹配“”java.lang.Object [] getObjects()"。
3. 也可以使用构造函数的短类名(不带包)或完整的类名来指定构造函数。与Java语言一样,构造函数规范具有参数列表,但没有返回类型。
4. 允许组合多个类成员访问修饰符标志(例如public static)。
复制代码
ProGuard 其他注意事项
-keepclasseswithmembernames class * {
native ;
}
复制代码
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
复制代码
ProGuard 的一些技术问题
在使用 ProGuard 时,您应该注意一些技术问题,所有这些问题都可以轻松避免或解决。在 ProGuard 处理代码时,它可能会打印一些警告和非致命警告:
ProGuard 列出了动态创建的类实例的所有类转换,例如“(MyClass)Class.forName(variable).newInstance()”。我们可能需要使用诸如“-keep class MyClass”之类的选项来保留提到的类,或者使用“-keep class * implements MyClass”来保留其实现。
ProGuard 列出了许多结构,例如“.getField("myField")”。我们可能需要弄清楚所提到的类成员是在哪里定义的,并使用“-keep class MyClass {MyFieldType myField;}”之类的选项来保留它们。否则,ProGuard 可能会删除或混淆类成员。
"-keep class mypackage.MyClass"
"-keep class * implements mypackage.MyInterface".
复制代码
"-keep class mypackage.MyClass { void myMethod(); }"
复制代码
更具体地说,如果报告为缺失的方法是 value 或 valueOf,则可能必须保留一些与枚举相关的方法。
总结起来就是:
一个通用的 ProGuard 混淆文件
最后,提供了更通用的 ProGuard 混淆文件参考。我们保留应用程序的 AndroidManifest.xml 文件可能引用的所有基类。如果清单文件收录其他类和方法,则可能还必须指定它们。
我们保留注释,因为它们可能被自定义 RemoteView 使用。
我们将使用典型的构造函数保留所有自定义视图扩展和其他类,因为它们可能会从 XML 布局文件中引用。
我们还在 Parcelable 或 Serializable 实现中保留所需的静态字段,因为它们可以通过自省来访问。
最后,我们保留了自动生成的 R 类引用内部类的静态字段,以便调用代码可以通过自省访问这些字段。
如果您使用的是 Google 的可选许可证验证库,您可以使用自己的代码来混淆它的代码。您必须保留其 ILicensingService 接口才能使库正常工作:
-keep public interface com.android.vending.licensing.ILicensingService
复制代码
如果您使用的是 Android 兼容性库,则应添加以下行以让 ProGuard 知道该库引用了某些类,这些类并非适用于所有版本的 API:
-dontwarn android.support.**
复制代码
必须保留“异常”属性以让编译器知道哪些方法可能会抛出异常。
如果动态调用任何其他非公共类或方法,则只能使用附加的 -keep 选项指定它们。
对于可以从库外部引用的任何内部类,还必须保留“InnerClasses”属性。否则,javac 编译器将无法找到内部类。
在 JDK 5.0 及更高版本中编译时,需要“签名”属性才能访问泛型。最后,我们保留“已弃用”属性和用于生成有用堆栈跟踪的属性。
-keepattributes Exceptions,InnerClasses,Signature,Deprecated
复制代码
另外,正规的第三方库一般都会在访问文档中写好需要的混淆规则,使用第三方库的时候要注意添加。在 WebView 中从 JavaScript 调用方法时,也需要保留它。Layout布局使用的View构造函数、android:onClick等也需要保留。
#指定要执行的优化遍数
-optimizationpasses 5
#混淆时不生成大小写混合的类名,即全部小写
-dontusemixedcaseclassnames
#指定不忽略非公共的库的类
-dontskipnonpubliclibraryclasses
#指定不忽略包可见的库类成员(字段和方法)。
-dontskipnonpubliclibraryclassmembers
#把混淆类中的方法名也混淆了
#为需要混淆的类生成唯一的混淆名称
-useuniqueclassmembernames
#关闭预验证
-dontpreverify
# 打印过程日志,在处理期间输出更多信息
-verbose
#-dontshrink #禁用压缩
#指定优化算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#关闭优化
-dontoptimize
#扩大类和类成员的访问权限,使优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
#四大组件和Application的子类不被混淆
-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
#如果使用的是Google的可选许可证验证库,则可以将其代码与自己的代码混淆。 必须保留其ILicensingService接口以使库正常工作
-keep public interface com.android.vending.licensing.ILicensingService
#将混淆堆栈跟踪文件来源重命名为“SourceFile”
-renamesourcefileattribute SourceFile
#保护注解。如果代码依赖注释,则可能需要保留注释,典型应用EventBus的事件接收回调
-keepattributes *Annotation*
#保留源文件名,变量名和行号,以产生有用的混淆堆栈跟踪
-keepattributes SourceFile,LineNumberTable
#保留异常,内部类/接口,泛型,Deprecated不推荐的方法
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,EnclosingMethod
#如果引用了v4或者v7包, 不报警告,使ProGuard知道该库引用了并非所有版本的API都可用的某些类
-dontwarn android.support.**
#保留native方法
-keepclasseswithmembernames class * {
native ;
}
#保留自定义View的类及构造函数,以使它们可以被XML布局文件引用
-keep class * extends android.view.View {
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
#保留自定义View的get和set相关方法
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#保持Activity中View及其子类为入参的方法,比如android:onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#保留符合指定构造函数类型的自定义控件类,如果和下面的写在一起,那么只有同时有这两类构造函数的类才满足
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet, int);
}
#保留R文件的静态成员,以使调用代码通过自省访问这些字段
-keepclassmembers class **.R$* {
public static ;
}
#保留枚举
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#保留实现了Parcelable接口的类中的静态成员
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#保持所有实现Serializable接口的类成员
-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();
}
#Fragment不需要在AndroidManifest.xml中注册,需要额外保护下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment
#指定不混淆指定的包名称
-keeppackagenames com.milanac007.*
#指定包名下的文件都保留
-keep class com.milanac007..blecommsdk.**{*;}
复制代码