概述
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反射的使用和混淆配置踩坑结束!
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!
最后
以上就是俏皮母鸡为你收集整理的完美解决kotlin反射提示java.lang.IllegalStateException: No BuiltInsLoader implementation was found错误的全部内容,希望文章能够帮你解决完美解决kotlin反射提示java.lang.IllegalStateException: No BuiltInsLoader implementation was found错误所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复