完美解決kotlin反射提示java.lang.IllegalStateException: No BuiltInsLoader implementation was found錯誤

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反射的使用和混淆配置踩坑結束!


如果你覺得本文對你有幫助,麻煩動動手指頂一下,算是對本文的一個認可,如果文中有什麼錯誤的地方,還望指正,轉載請註明轉自喻志強的博客 ,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章