Android 从混淆引发的问题谈起

从事Android 已有2 、3年光景,但一直没有深入了解过混淆,和深入学习探究过混淆,只是知道有这么一个topic,直到前些天,遇到了一个问题 下定决心系统学习相关知识点。问题来源:自己再debug状态下没有任何问题,但是打了release包就有问题,要不是闪退,要不就是网络请求没有效果,数据请求错误。然后自己把release的混淆关闭,一切也没有问题,这个时候可以锁定问题出在混淆上了。

于是乎,开始学习混淆方面的知识点,那我们知道混淆就是加固app,还有减少不必要的代码,优化代码,混淆代码,如下图:

This scheme shows how ProGuard shrinks, optimizes, obfuscates and preverifies Java code.

这里的问题肯定是和代码混淆有关,开始排查吧。

当时,debug包一切都好,release包则是闪退,闪退的原因是 用反射获取的类没有找到,然后我弃用了反射方法,改用其他的方法,然后接着是数据请求数据获取失败,然后chalse 抓包,服务端数据已经返回到app端,但数据就是获取不到,很可能就是json转化错误,由于之前对混淆不熟,但也有一些了解,当这三点现象一起出现,我确定是混淆引起的,于是重新排查混淆规则,发现我把baseResp类写到了 基础公共模块里,忘记添加keep 此类,然后添加规则,数据就有了,在把反射方法改回来,也是ok的。困扰了2天的问题就解决了,于是系统的记录有关混淆知识点。

 

参考文档

google 文档:https://developer.android.com/studio/build/shrink-code#keep-code
proguard网址: https://www.guardsquare.com/en/products/proguard/manual/usage

proguard 和google 的关系是什么呢? proguard 是一家专门从事app 加固的公司,有相关的技术;而google 再android相关的加固技术采用了这家公司的技术,于是我们再app的gradle 文件中可以看到相关的声明:

release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.Release_Key
        }

这样两家公司其实一种合作关系,类似的这种合作还有很多,并不是android里面的所有知识都是google 开发的,比如构建工具gradle 也不是google公司的,androidStudio 开发工具也是购买Intellij 公司的产品,二次开发专门针对android开发者的。

先看看关于proguard 网站的介绍,我们需要学习混淆语法,混淆哪些类,混淆注意事项,其他相关的混淆知识点。

混淆语法:

混淆采取的非黑即白的做法,当我们开启混淆后,默认一切代码都混淆,但混淆会引起一些问题,比如反射、数据转换(反射是靠字符串寻找类,若把类名混淆则反射时就无法找到)。这时候我们需要指明哪些不需要混淆。

保留防止被移除或者被重命名防止被重命名
类和类成员-keep-keepnames
仅类成员-keepclassmembers-keepclassmembernames
如果拥有某成员,保留类和类成员-keepclasseswithmembers-keepclasseswithmembernames

上面表格,发现其功能很相似,可以见名知意,我们关注第二列,因为第二列的功能包含了第三列,再用到自己的代码中,若不追求极致混淆,我们可以一律用第二列的替代第三列。

混淆公式:

[保持命令] [类] {
    [成员] 
}

 “类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:

  • 具体的类
  • 访问修饰符(publicprotectedprivate
  • 通配符*,匹配任意长度字符,但不含包名分隔符(.)
  • 通配符**,匹配任意长度字符,并且包含包名分隔符(.)
  • extends,即可以指定类的基类
  • implement,匹配实现了某接口的类
  • $,内部类

“成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:

  • <init> 匹配所有构造器
  • <fields> 匹配所有域
  • <methods> 匹配所有方法
  • 通配符*,匹配任意长度字符,但不含包名分隔符(.)
  • 通配符**,匹配任意长度字符,并且包含包名分隔符(.)
  • 通配符***,匹配任意参数类型
  • ,匹配任意长度的任意类型参数。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 这些方法。
  • 访问修饰符(publicprotectedprivate

接下来,一些场景的应用场景,掌握这几种就可以了;

混淆一个包下的所有类(不包括子包)

-keep class com.sjh.proguraddemo.* { *;}  //com.sjh.proguraddemo 为文件路径(也是包名)

混淆一个包下的所有类(包括子包)

-keep class com.sjh.proguraddemo.** { *;}  //com.sjh.proguraddemo 为文件路径(也是包名)

对比这两个,后面比前面的多了一个 * ,因为一个* 不能匹配. 所以无法跨越子包,通常为了方便省事情,后者用的更多 ; 另外, {*;} 表示类里面的所有属性、方法不混淆。

另外,类前面可以加修饰符,pulic private 等等,当然了一般外部类都是public,

不混淆所有的构造方法

-keep public class com.sjh.demo.baseClass {
  // 该类下的所有构造函数不混淆,同理还有 字段、方法
 [访问修饰符]   <init>                              
}

针对第三方jar,aar等第三方库引用文件,一般库提供者会提供混淆代码,直接复制过来ok,但是有些没有提供的,一般采取均不混淆,直接找到第三方库的跟目录,然后:

-keep class com.xx.xx.** {*;}   //包的目录

 针对某个特定的类(Constants)不混淆:(直接写出类的全路径名即可)

-keep class com.sjh.proguarddemo.Constants { *; }

 不混淆某个类的子类或者实现某个接口的类

-keep class * extends com.sjh.proguarddemo.BaseClass{ *; }       //类

-keep class * implement com.sjh.proguarddemo.BaseInterface{ *; }  // 接口

不混淆某个类的特定方法

-keepclassmembers class com.sjh.proguarddemo.BaseClass { 
    public void method(java.lang.String);  // 括号里要指明参数类型,或者用 ... 表示无论什么类型 
                                             // 参数 
}

不混淆内部类

-keep class com.sjh.proguarddemo.BaseClass$* {
        *;
 }

知道以上用法就够了,当然,网上有很多的那种混淆公共模板,我们可以复制到工程中。

哪些类不要混淆

  •  Android系统组件
  • Parcelable ,需要使用序列化的
  • java 序列化
  • 枚举
  • native 方法
  • 注释 annotations
  • 用到反射的地方
  • 解析服务器数据的model
  • ...

要知道为什么这些类不能混淆,我们知道混淆的根本就是把之前的类名、方法名、属性名用无意义的字符串替换,那凡是依赖这些特定的字符串的地方就都不能混淆,比如说数据类model,解释可以 https://medium.com/androiddevelopers/practical-proguard-rules-examples-5640a3907dc9 ;

混淆的多工程配置问题

在模块化开发中,我们有多个module, 我们需要对每个module 进行单独配置混淆文件,不要统一在 主工程中统一配置,这样其实有了关联,最好不要有关联。至于这个问题,有很多人已经谈到过,

先看一张图

其gradle 语法变了,官网介绍 。可以参考 点击1 、点击2 。

混淆文件Mapping.txt

如果开启混淆后,在我们构建app时输出相应的混淆映射文件

这个mapping 有什么用呢?比如说,我们app用了第三方Crash日志收集平台,当我们开启了混淆时,我们在其平台上看到的日志信息可能是大量的无意义的符号,没有说出真正的崩溃类,这时候我们就需要把这个mapping文件上传上去,平台会根据这个文件把日志还原,开发人员就可以看到真正有意义的日志了。

混淆工具

每个平台都有最常规的混淆工具 , java代码常用的是ProGuard ,Python有python的,其实还有很多的混淆工具,这个后面详细解析。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值