首页 > 其他分享 >kotlin 和 r8 的量子纠缠 | 类加载机制偷鸡

kotlin 和 r8 的量子纠缠 | 类加载机制偷鸡

时间:2023-06-19 12:07:02浏览次数:39  
标签:r8 偷鸡 kotlin R8 jar Kotlin AGP 加载


前言

戏接上文,kotlin升级没想到啊还有一个大坑。我们之前说了我们使用的agp版本是7.0.3,在这个版本的R8竟然会出现kotlin混淆的bug。

断更一个月,不更文的一个原因就是因为最近感觉太菜了,并没有文章素材了。

问题排查

接下来还是一点点进行问题分析,我们先从kotlin元数据开始讲这个问题。

元数据

大家可以参考下官方的这篇文章R8 编译器: 为 Kotlin 库和应用 “瘦身”。

kotlin中的一部分类信息都会生成在Metadata注解中,(Metadata就是kotlin元数据)。另外工程内有一部分代码使用了kotlin-reflect的能力,而kotlin-reflect很多能力都是通过读取元数据来完成的。

Kotlin 元数据 是存储在 Java 类文件的注解中的一些额外信息,它由 Kotlin JVM 编译器生成。元数据确定了类文件中的类和方法是由哪些 Kotlin 代码构成的。比如,Kotlin 元数据可以告诉 Kotlin 编译器类文件中的一个方法实际上是 Kotlin 扩展函数。

kotlin 和 r8 的量子纠缠 | 类加载机制偷鸡_android

这个是我通过jadx反编译出来的一个类的信息,我们可以发现很多关键信息都存放在元数据中。其中如果元数据丢失了可能就会影响到的就是一些kotlin和java的互相调用,还有就是一些kotlin-reflect的调用。

但是我们在release混淆包中,这部分kotlin 1.7.10生成出来的元数据竟然被R8代码优化掉了,导致了release包的部分功能异常。

Gradle中的类加载机制

这里要展开这个可能会比较突兀哦,但是其实大家可以继续向下看下去就知道了。

JVM类加载机制、双亲委派和SPI机制

面试中我们经常被问到的一个问题就是类的生命周期,以前的时候我对于这个东西是没有什么概念的,因为毕竟没有什么实际的应用场景,但是这里雀食是有的。

kotlin 和 r8 的量子纠缠 | 类加载机制偷鸡_java_02

上图就是类的生命周期了,类加载机制有个特性,如果当前的ClassLoader内已经加载过这个类则后续就会使用这个类去完成构造,当然如果不存在则会去挂载jar,然后从jar中去构造出。当然我们一般在写安卓的时候很少会出现加载两个不同版本的jar的情况,但是这个在Gradle编译中是被允许的,所以先后加载jar的顺序就决定了我们会使用哪个版本的jar

我们之前就写过一个很意思的bug,我们在Settings插件内先加载了低版本的AGP,之后我们即时在build.gradle内定义了高版本的AGP,因为类加载机制的原因,也会把AGP锁定在一个低版本上,因为这个jar已经被ClassLoader优先加载了。

单独升级R8

接下来我们就需要偷偷的使用上面的方法,跳过AGP 7.0.3中低版本的R8,直接使用高版本AGP 7.2.1R8就能修复这个异常了。

正常情况下我们都是在build.gradle内的buildscript去定义AGP版本的。这次我们只需要把这个R8的版本放到settings.gralde中就可以解决这个问题了。

buildscript{
  dependencies {
         classpath("com.android.tools:r8:3.2.60")
         classpath('com.google.guava:guava:30.1.1-jre')
  }
}

当然大部分情况下其实我是不建议使用这种黑魔法的,因为经常会出现方法签名等等匹配不上的情况。而R8因为了其中有中间层的特殊性,所以可以比较容易的被替换成另外一个版本。

总结

全TM是坑啊,其实还有好几个问题我都没说。只能说世事无常大肠包小肠。

另外因为我们有一部分方法签名检查的a8就是基于r8开发的,所以后面就可能还有一篇吧。

我打算后续吹嘘下Gradle Enterprise,试用阶段发现真的还是挺好用的。

参考文献

Data class metadata is removed with proguard / R8 for Kotlin 1.6.0

R8 编译器: 为 Kotlin 库和应用 “瘦身”

作者:究极逮虾户


标签:r8,偷鸡,kotlin,R8,jar,Kotlin,AGP,加载
From: https://blog.51cto.com/u_16163442/6512293

相关文章

  • 使用Kotlin+Rretrofit+rxjava+设计模式+MVP封装网络请求
    0、前言:kotlin使用起来非常顺畅,尤其是结合rxjava、设计模式使用,你会发现写代码原来可以这么开心!什么?你还在使用java?赶紧去学一下kotlin吧!我相信你一定会对他爱不释手,kotlin也很容易学,花一天的时间就可以从java切换为kotlin一、正文本文主要介绍如何使用kotlin封装网络请求的工具,结......
  • Kotlin版本的WanAndroid项目实战(三):Kotlin的集合
    集合概述学习方法:扫描一遍下述表格中集合相关的有哪些操作,具体使用时再可以详细去查构造集合Kotlin里面添加了可变集合的概念,目前只是一种接口级别的限制,底层实现还是可变的集合,不是线程安全的,还是可以通过Java代码修改内部元素迭代器(1)Iterable接口的继承者(包括Set与......
  • Kotlin与Java互调原理项目实战
    数天前我将我java开发的工程,全部转换成了kotlin形式的工程。如果你也想做,本身也有一定的java开发安卓程序的功底。本文将比较适合你。创建kotlin工程,拷贝类文件xml文件等核心文件到工程目录下,形成一个kotlin底子的java代码组成的工程,然后通过ctrl+shift+alt+k快捷代码逐个转换......
  • kotlin协程原理分析项目实战
    自从6.0加入动态权限之后,很多地方都要用到,开始的时候使用的原生代码写权限请求,代码格式如:if(应用是否已经请求了该权限组){权限请求后的代码}else{请求权限}然后不知道在fragment或是activity里面重写overrideonRequestPermissionsResult{if(请求成功){......
  • 用kotlin来开发一个cli工具 | 没用的技能+1
    脚手架脚手架是为了保证各施工过程顺利进行而搭设的工作平台而在程序开发过程中,每个工程或者说公司也都需要一个脚手架工具。通过脚手架命令行的形式简化开发流程,避免发生一些人为的相对低级的问题,所以这个也就是为什么叫做脚手架的原因吧。而由于每个公司的代码规范都不同,一般情况......
  • Kotlin协程-从理论到实战
    上一篇文章从理论上对Kotlin协程进行了部分说明,本文将在上一篇的基础上,从实战出发,继续协程之旅。从源头说起在Kotlin中,要想使用协程,首先需要使用协程创建器创建,但还有个前提——协程作用域(CoroutineScope)。在早期的Kotlin实现中,协程创建器是一等函数,也就是说我们随时随地可......
  • Kotlin协程-从一到多
    上一篇文章,我介绍了Kotlin协程的创建,使用,协作等内容。本篇将引入更多的使用场景,继续带你走进协程世界。使用协程处理异步数据流常用编程语言都会内置对同一类型不同对象的数据集表示,我们通常称之为容器类。不同的容器类适用于不同的使用场景。Kotlin的Flow就是在异步计算的需......
  • Kotlin协程-那些理不清乱不明的关系
    Kotlin的协程自推出以来,受到了越来越多Android开发者的追捧。另一方面由于它庞大的API,也将相当一部分开发者拒之门外。本篇试图从协程的几个重要概念入手,在复杂API中还原出它本来的面目,以全新的角度带读者走进Kotlin协程世界。什么是协程在很多有关协程的文章中,描述协程通常会......
  • Kotlin 集合对象的单条件和多条件排序
    原文:Kotlin集合对象的单条件和多条件排序-Stars-One的杂货小窝本文不是太难的东西,因为sortedWith之前没怎么用过,所以就记录下平常开发经常使用到List,Map等数据集合类型,也会经常遇到排序的问题,可以直接使用sortedBy或sortedByDescending排序多条件则是使用sortedWith,......
  • MUR80120PT-ASEMI大电流快恢复二极管MUR80120PT
    编辑:llMUR80120PT-ASEMI大电流快恢复二极管MUR80120PT型号:MUR80120PT品牌:ASEMI封装:TO-247正向电流:80A反向电压:1200V引脚数量:3恢复时间:35ns正向压降:1.8V类型:快恢复二极管特性:超快恢复二极管、功率二极管工作温度:-50°C~150°C封装尺寸:如图特性:大电流、快恢复二极管M......