Android proguard语法简单总结

Proguard是一个代码优化和混淆工具。
能够提供对Java类文件的压缩、优化、混淆,和预校验。压缩的步骤是检测并移除未使用的类、字段、方法和属性。优化的步骤是分析和优化方法的字节码。混淆的步骤是使用短的毫无意义的名称重命名剩余的类、字段和方法。压缩、优化、混淆使得代码更小,更高效。
所以proguard不只是用于混淆。
关于proguard的使用我就不提了,这里只总结常用语法

## ------------------------------------- 混淆基础配置 ---------------------------------------------
-optimizationpasses 5                               # 指定代码的压缩级别
-dontusemixedcaseclassnames                         # 混淆时不会产生形形色色的类名
-dontskipnonpubliclibraryclasses                    # 指定不去忽略非公共的库类
-dontskipnonpubliclibraryclassmembers               # 指定不去忽略包可见的库类的成员
-dontpreverify                                      # 不预校验
-ignorewarnings                                     # 屏蔽警告
-verbose                                            # 混淆时记录日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*    #优化
-keepattributes *Annotation*                        # 保护代码中的Annotation不被混淆
-keepattributes Signature                           # 避免混淆泛型, 这在JSON实体映射时非常重要
-keepattributes SourceFile,LineNumberTable          # 抛出异常时保留代码行号
# 异常
-keepattributes Exceptions
#保留包裹类定义的直接方法
-keepattributes EnclosingMethod

//例如
public class Bug {


    public static Object getBug(){
        class ClassA{

        };
        return new ClassA();
    }
}
//获取classA的直接包裹方法
{
        Class<?> aClass = Bug.getBug().getClass();
        Log.d("tedu", "onCreate: "+aClass.getEnclosingMethod());
}
//如果没有使用-keepattributes EnclosingMethod 就会返回null


-dontnote [class_filter]

不输出指定类的错误信息.
-dontwarn [class_filter]

不打印指定类的警告信息

-assumenosideeffects class_specification

可以指定移除哪些方法没有副作用,如在android开发中,如想在release版本可以把所有log输出都移除,可以配置:

 # 在优化阶段移除相关方法的调用
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

这里写图片描述

keep和keepname的区别就是remove和name的区别了,具体看上图。

-keep 指定类和类成员(变量和方法)不被混淆。
    -keep class com.dongnao.proxy.guard.test.Bug
    (保护了类名)
    -keep class com.dongnao.proxy.guard.test.Bug{
        public static void *();
    }
    (保护了 public static void的没有参数的函数)
    -keep class com.dongnao.proxy.guard.test.Bug{
        *;
    }
    (保护所有)
-keepclassmembers 指定类成员不被混淆(就是-keep的缩小版,不管类名了)。
    -keepclassmembers class com.dongnao.proxy.guard.test.Bug
    (这里没有写任何类成员,所以都被混淆了,相当于没用)
-keepclasseswithmembers 指定类和类成员不被混淆,前提是指定的类成员存在。
    -keepclasseswithmembers class   com.dongnao.proxy.guard.test.Bug
    (保护类名,但是没指定成员,所以函数名被混淆)
    -keepclasseswithmembers class       com.dongnao.proxy.guard.test.Bug{
        native <methods>;
    }
    全被混淆了。注意 前提是指定的类成员存在 如果不存在native函数,所以这条语句等于无效,

混淆移除过滤具体可以参考这篇文章https://mp.weixin.qq.com/s/DE4gr8cTRQp2jQq3c6wGHQ

关键字和通配符

参考http://ju.outofmemory.cn/entry/233480

表达式中的 class 关键字表示任何接口类、抽象类和普通类; interface 关键字表示只能是接口类; enum 关键字表示只能是枚举类。如果在 interface 和 enum 关键字前面加上感叹号(“ ! ”)分别表示不是接口类的类和不是枚举类的类。 class 关键字前面是不能加感叹号的。

对于类名( classname )来说,可以是类全名,或者可以包含以下一些特殊字符的正则表达式:

1) ? :问好代表一个任意字符,但不能是句号(“ . ”,因为句号是包名分隔符);

2) * :单个星号代表任意个任意字符,但不能代表句号;

3) ** :两个星号代表任意个任意字符,且能代表句号。
对于单个星号来说,如果类名部分只有一个星号,不包含其它任何字符,为了保证兼容性,其代表任何类,就跟两个星号的作用一样了。

extends 和 implements 表示限定类一定要扩展自一个指定类或者实现了一个指定接口类,这时候通常类名部分是一个星号。

对于类中的成员变量(Fields)来说,可以通过变量类型 fieldtype 和变量名 fieldname 来精确指定,也可以通过 表示类中的任何成员变量。

对于类中的成员函数(Methods)来说,可以通过返回类型 returntype 、方法名 methodname 和参数类型 argumenttype 来唯一限定,也可以通过 来表示类中的任何成员函数。

对于类的构造函数来说,可以用 加上构造函数的参数来指定。

星号(“ * ”)可以匹配类中的任何成员变量和函数。

对于类中的成员函数名 methodname 和成员变量名 fieldname 来说也可以使用通配符来匹配,同样问号(“ ? ”)可以匹配一个任意字符,而星号(“ * ”)可以匹配任意多个任意字符。

对于类中的成员变量的类型、成员函数的返回类型和参数类型,以及构造函数的参数类型来说,可以使用下面这些通配符来匹配:

1) % :匹配任何原始类型,如 boolean 、 int 等,但不包括 void ;

2) ? :匹配一个任意字符,不包括句号;

3) * :匹配任意个任意字符,不包括句号;

4) ** :匹配任意个任意字符,包括句号;

5) * :匹配任意类型,包括原始类型和非原始类型,数组类型和非数组类型;

6) … :匹配任何数目个任何类型的参数。

在类名前、类中成员变量和成员函数名前,可以加上访问限定符(如 public 、 private 、 protected 等,修饰类、成员变量和成员函数的访问限定符各不相同)。如果加上了访问限定符后,就表示要匹配的类、成员变量或成员函数的定义中必须包含这些限定符。如果在限定符前面加上感叹号“ ! ”,则刚好相反,定义中必须不包含这些限定符

$ 在类名后表示内部类

<init> 匹配了构造方法;
<fields>匹配了成员变量;
<methods>匹配了方法;

额外总结
1

关于glide中使用的混淆保留了内部枚举类
例如:

-keep public enum com.stuff.MyConfigObject$** {
    **[] $VALUES;
    public *;
}

这样将保留所有MyConfigObject的内部枚举,至于**[] $VALUES,枚举被编译后编程类似于这样

public enum Stuff extends Enum<Stuff> {
    /*public static final*/ COW /* = new Stuff("COW", 0) */,
    /*public static final*/ POTATO /* = new Stuff("POTATO", 1) */,
    /*public static final*/ MOUSE /* = new Stuff("MOUSE", 2) */;
    /*synthetic*/ private static final Stuff[] $VALUES = new Stuff[]{Stuff.COW, Stuff.POTATO, Stuff.MOUSE};

    public static Stuff[] values() {
        return (Stuff[])$VALUES.clone();
    }

    public static Stuff valueOf(String name) {
        return (Stuff)Enum.valueOf(Stuff.class, name);
    }

    private Stuff(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) {
        super($enum$name, $enum$ordinal);
    }
}
2

在Android studio中
开启混淆后有一个默认文件和一个指定的文件
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
默认文件的需要首先执行gradle命令extractProguardFiles,然后会在跟目录下的build/intermediates/proguard-files中查看默认的proguard文件。例如build/intermediates/proguard-files/proguard-android.txt-3.0.1,该文件指定了一些常用的规则,而最终混淆是通过两个文件综合规则来进行混淆的。

常用的大概就这些了,其他不常用的开发基本用不到,如果有什么不对的或者补充的,欢迎评论补充,一起学习进步。

猜你喜欢

转载自blog.csdn.net/a568478312/article/details/79652048
今日推荐