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正常運行,又不影響混淆的效果!
好了,關於kotlin反射的使用和混淆配置踩坑結束!
如果你覺得本文對你有幫助,麻煩動動手指頂一下,算是對本文的一個認可,如果文中有什麼錯誤的地方,還望指正,轉載請註明轉自喻志強的博客 ,謝謝!