如何在Android应用中运行KotlinScriptEngine并解决权限报错
解决方案:Android上运行Kotlin脚本引擎规避Non-SDK接口限制
核心问题分析
你遇到的错误源于Android 12(API 31)及以上版本对Non-SDK接口的严格限制,Kotlin官方的JSR223脚本引擎依赖sun.misc.Unsafe的getAndAddInt等隐藏方法,这些方法在targetSdk 34下被完全禁止调用,导致编译器初始化失败。以下是几种可行的解决方案:
方案1:临时降级targetSdk版本(快速验证)
如果允许暂时降低targetSdk,可以将build.gradle中的targetSdk改为32:
android { defaultConfig { targetSdk = 32 } }
此方案能直接绕过Android 12+的Non-SDK限制,和Code Assist的运行环境一致,但无法适配Android 13/14的新特性。
方案2:使用适配Android的Kotlin脚本引擎依赖
替换官方的kotlin-compiler-embeddable为第三方适配过的版本,这些版本通常移除了对sun.misc.Unsafe的依赖:
// 移除原有的runtimeOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable") implementation("com.github.prof18:kotlin-scripting-android:1.0.0") implementation(kotlin("scripting-jsr223")) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
然后修改脚本执行代码,适配Android类加载器:
import javax.script.ScriptEngineManager val engineManager = ScriptEngineManager() val engine = engineManager.getEngineByExtension("kts") // 绑定当前应用的类加载器 engine.context.classLoader = this.classLoader engine.eval(kotlinCode)
方案3:自定义Kotlin脚本执行逻辑(无JSR223依赖)
如果需要更灵活的控制,可以直接使用Kotlin Scripting核心API,避免JSR223封装带来的隐藏依赖:
- 添加Gradle依赖:
implementation("org.jetbrains.kotlin:kotlin-scripting-common") implementation("org.jetbrains.kotlin:kotlin-scripting-jvm") implementation("org.jetbrains.kotlin:kotlin-scripting-compiler-impl")
- 自定义脚本执行代码:
import kotlin.script.experimental.api.* import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.jvm.* // 配置脚本编译环境 val scriptCompilationConfig = ScriptCompilationConfiguration { defaultImports(listOf("kotlin.*")) jvm { dependenciesFromCurrentContext(wholeClasspath = true) } } // 执行表达式脚本 suspend fun runExpression(expression: String, a: Int, b: Int): ResultValue { val scriptCode = """ fun compute(a: Int, b: Int) = $expression compute($a, $b) """.trimIndent() return runScripting { scriptCode.toScriptSource().evaluate( compilationConfiguration = scriptCompilationConfig, environment = ScriptEvaluationConfiguration { jvm { classLoader = this@YourActivity.classLoader } } ) } }
此方案完全脱离JSR223,减少了对隐藏API的依赖,更适配Android环境。
方案4:添加Hidden API豁免(不推荐生产环境)
如果必须使用官方引擎且保持targetSdk34,可以通过Android的Hidden API豁免清单解除对特定方法的限制:
- 在
src/main/res/xml/下创建hiddenapi-package-whitelist.xml:
<?xml version="1.0" encoding="utf-8"?> <hidden-api-whitelist> <method signature="Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I" /> <field signature="Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;" /> </hidden-api-whitelist>
- 在
AndroidManifest.xml中引用该清单:
<application> <meta-data android:name="android.app.hiddenapi.whitelist.xml" android:resource="@xml/hiddenapi_package_whitelist" /> </application>
注意:此方法依赖Android私有API豁免机制,可能在未来版本被废弃,且无法通过Google Play审核。
额外注意事项
- 脚本编译属于耗时操作,必须放到后台线程(如使用Coroutine),避免主线程ANR
- 对用户输入的表达式做语法限制,仅允许算术运算、基本Kotlin语法,防止恶意代码执行
内容的提问来源于stack exchange,提问作者Lavish Swarnkar
相关产品推荐
相关产品推荐

