完美解决kotlin反射提示java.lang.IllegalStateException: No BuiltInsLoader implementation was found错误

版权声明:本文为博主原创文章, 转载请注明本文转自 [喻志强的博客](https://blog.csdn.net/yuzhiqiang_1993)。如果文中有什么纰漏或错误的话,请留言指正,我会及时更正。如果您觉得本文还不错的话,记得点个赞呦,希望能帮到你,谢谢。 https://blog.csdn.net/yuzhiqiang_1993/article/details/88949003

Kotlin 使用反射获取对象的属性及属性值

由于项目需求,需要对对象进行反射并获取对象的属性名称以及对应的属性值,下面是通过Kotlin反射获取对象的属性和属性值的代码:

首先是数据类:
一个简单的Person类,两个属性

data class Person(var name: String,var age: Int)

下面是获取通过反射Person对象获取属性名称和对应的属性值;

import kotlin.reflect.full.memberProperties

fun main(args: Array<String>) {
    
    /*创建一个Person对象*/
    val p = Person("yzq", 25)

    /*返回在该类及其所有超类中声明的非扩展属性*/
    val memberProperties = p.javaClass.kotlin.memberProperties
    
    /**
     * 迭代出反射对象的属性名称以及属性值
     */
    memberProperties.forEach {
        println("${it.name}:${it.call(p)}")
    }
}

运行后打印的日志:
在这里插入图片描述
嗯,可以看到我们已经正常的通过反射拿到属性名称和属性值了,美滋滋。
你以为这样就结束了吗?
不不不!! 后面还有坑呢。


java.lang.IllegalStateException: No BuiltInsLoader implementation was found.

美滋滋的用完反射后,打包出来运行后发现报错了,报错信息如下:

java.lang.IllegalStateException: No BuiltInsLoader implementation was found. Please ensure that the META-INF/services/ is not stripped from your application and that the Java virtual machine is not running under a security manager

大概意思是:没有找到BuiltInsLoader实现,跟踪错误信息后发现是使用对象反射的地方报的错误,第一反应是应该是混淆导致的问题。于是乎执行了一个面向谷歌/百度编程的操作,看到了这篇文章https://discuss.multi-os-engine.org/t/notes-on-proguard-when-using-kotlin-reflect-like-jackson-kotlin/891

解决办法一(不推荐)

在混淆配置文件中添加下面的配置即可

-keepattributes *Annotation*  
-keep class kotlin.** { *; }
-keep class org.jetbrains.** { *; }

加上后运行确实没问题了,但是,上面的混淆表示所有的kotlin类都不进行混淆,那么,混淆效果就大打折扣。很多kotlin的类都没有被混淆,对apk反编译后看到确实很多kotlin类都没有被混淆,那这不是拆了东墙补西墙吗,如果你对app的混淆要求较高,这个方案不推荐

解决办法二

既然报错信息提示我们没有找到BuiltInsLoader实现,而我们又不想影响混淆的效果,那我们只能减小不混淆的范围。
后来又看到了这个:https://github.com/square/moshi/issues/402

将上面的混淆配置修改为下面的混淆配置即可
我们不混淆BuiltInsLoader相关代码,其他的还是正常混淆

-keep interface kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader
-keep class kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl

打包后反编译发现混淆正常了,美滋滋!

你以为这样就结束了了吗?
不不不!!!
运行的时候又报错啦!
在这里插入图片描述

报错信息大概如下

java.lang.IllegalAccessException: Class java.lang.Class<kotlin.reflect.jvm.internal.a.e$d> cannot access private field java.lang

嗯,这个我大概看明白了,就是说无法访问私有字段
这不对啊,kotlin默认修饰符不就是public吗,而且我又没有指定我要反射的类的属性为private,怎么会出现访问不到私有字段呢?

于是乎赶紧反编译查看我要反射的类,图示如下
在这里插入图片描述

反编译后查看代码发现,kotlin编译成java类时自动给属性加上了private修饰符!

在这里插入图片描述
真是不看不知道,一看吓一跳啊!
在这里插入图片描述
嗯,这操作给满分!

知道原因后,那就好办了,下面是两个解决办法,推荐第二个方案

方案一
我们都知道kotlin给我们提供了一些jvm相关的注解,我们去瞅瞅官方文档,看看有没有解决办法
Kotlin Jvm注解官方文档

英文太烂,翻译一下看看

哎呦,JvmField这个不就是我需要的么

好嘞,加上注解再打包试试
在这里插入图片描述
然后再反编译看一下
在这里插入图片描述
如我所愿,属性现在是public的了,再运行App看一下,嗯,完全正常

这就达到了我的目的,即能保证app正常运行,又不影响混淆的效果!

方案二
这个最简单,直接在使用反射的时候允许访问私有属性即可
将isAccessible设置为true

isAccessible=true

代码如下

import kotlin.reflect.full.memberProperties
fun main(args: Array<String>) {
    /*创建一个Person对象*/
    val p = Person("yzq", 25)
    /*返回在该类及其所有超类中声明的非扩展属性*/
    val memberProperties = p.javaClass.kotlin.memberProperties
    /**
     * 迭代出反射对象的属性名称以及属性值
     */
    memberProperties.forEach { member ->
         member.isAccessible = true  //允许访问私有变量
        println("${member.name}:${member.call(p)}")
    }

}

这样一来,我们就不需要给实体类的属性加上@JvmField注解了!

好了,关于kotlin反射的使用和混淆配置踩坑结束!


如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

猜你喜欢

转载自blog.csdn.net/yuzhiqiang_1993/article/details/88949003