如何在IntelliJ插件中调用本地代码?JNI库分发问题咨询
核心问题分析
你当前代码里的System.loadLibrary("hello")需要对应平台的标准命名库(比如Linux是libhello.so、Windows是hello.dll、macOS是libhello.dylib),但你的库命名为mylib.*,首先要确保名称对应——要么修改库文件名,要么改用System.load()直接指定路径(更灵活,适合多架构分发)。
另外,直接把本地库放在src目录关联模块无法生效,因为JVM需要从磁盘加载本地库,打包进Jar的资源无法直接被System.loadLibrary()识别,必须通过Gradle配置打包资源,再在运行时提取到临时目录加载,或者配置JVM的java.library.path。
方案一:Gradle配置+运行时提取临时文件(推荐用于分发)
1. 调整项目资源结构
在src/main/resources下创建平台专属目录,存放对应架构的本地库:
src/main/resources/ └── native/ ├── linux-x86_64/ │ └── mylib.so ├── macos-x86_64/ │ └── mylib.dylib ├── macos-aarch64/ │ └── mylib.dylib └── windows-x86_64/ └── mylib.dll
2. 修改Gradle配置(build.gradle.kts)
确保native目录下的所有库都被打包进插件Jar:
sourceSets { main { resources { srcDirs("src/main/resources") include("native/**/*") // 包含所有本地库文件 } } }
3. 调整JNI加载代码
替换原有的init块,实现从Jar资源提取本地库到临时文件,再加载:
external fun hello(): Void companion object { init { // 识别当前运行平台 val osName = System.getProperty("os.name").lowercase() val osArch = System.getProperty("os.arch").lowercase() val (platformDir, libExtension) = when { osName.contains("win") -> "windows-$osArch" to "dll" osName.contains("mac") -> "macos-$osArch" to "dylib" osName.contains("nix") || osName.contains("nux") -> "linux-$osArch" to "so" else -> throw UnsupportedOperationException("Unsupported operating system") } val libResourcePath = "/native/$platformDir/mylib.$libExtension" // 从Jar中读取资源,写入临时文件 val tempLib = File.createTempFile("mylib", ".$libExtension").apply { deleteOnExit() // JVM退出时自动删除临时文件 javaClass.getResourceAsStream(libResourcePath).use { input -> outputStream().use { output -> input?.copyTo(output) ?: throw RuntimeException("Failed to load native library resource") } } } // 加载临时文件 System.load(tempLib.absolutePath) } }
方案二:Gradle配置java.library.path(仅适合开发阶段)
如果只是开发调试时需要加载本地库,可以直接配置JVM的java.library.path指向本地库目录,无需提取临时文件:
tasks.withType<JavaExec> { val nativeRoot = projectDir.resolve("src/main/resources/native") val osName = System.getProperty("os.name").lowercase() val osArch = System.getProperty("os.arch").lowercase() val platformDir = when { osName.contains("win") -> "windows-$osArch" osName.contains("mac") -> "macos-$osArch" osName.contains("nix") || osName.contains("nux") -> "linux-$osArch" else -> throw UnsupportedOperationException("Unsupported OS") } // 设置java.library.path为当前平台的本地库目录 systemProperty("java.library.path", nativeRoot.resolve(platformDir).absolutePath) }
此时代码可以保留原有的System.loadLibrary("mylib")(注意库名要对应,比如Linux下库文件需命名为libmylib.so)。
关于c-interop的说明
c-interop是Kotlin/Native用于直接调用C/C++库的机制,而IntelliJ插件运行在JVM上,属于Kotlin/JVM环境,因此c-interop不适用你的场景,JNI仍然是正确的技术选型。
内容的提问来源于stack exchange,提问作者Alexandr Dorofeev

