在 Ktor 中使用 JNI 庫
在Ktor
官方文件中,並沒有講述如何在 Ktor 內呼叫 JNI 庫,然而在實際開發中,用到 JNI 的場景還是相當多的,特別是團隊裡有那麼幾個寫 C++ 成癮並且希望什麼都走 JNI 的人的時候。
其實對於 JNI 的載入來說,問題永遠只有一個,就是路徑,把 JNI 放在哪裡才能正常載入呢?
按以往在 Java 上搞 JNI 的經驗來說,只要是java.library.path
可以指向的路徑均可,所以在 Ktor 下也可以如此進行,寫一些簡單的程式碼來看看這個路徑在哪:
get("/path") { call.respondText { System.getProperty("java.library.path") } }
然後在瀏覽器內請求http://localhost:8080/path
即可看到效果,得到的路徑為:
/Users/rarnu/Library/Java/Extensions /Library/Java/Extensions /Network/Library/Java/Extensions /System/Library/Java/Extensions /usr/lib/java .
以上路徑是在我自己的環境下得到的,你的環境或許會不同,不過無傷大雅。在路徑列表的最後是一個點,即代表當前路徑,換言之,將 JNI 庫放在專案的根目錄下是可行的。
那麼事不宜遲,隨手寫個 JNI 庫試試,為了簡單起見,直接從 JNI 返回傳入的字串。
#include <jni.h> extern "C" { JNIEXPORT jstring JNICALL Java_com_rarnu_sample_NativeSample_hello(JNIEnv* env, jclass obj, jstring text); }
#include "Sample.h" JNIEXPORT jstring JNICALL Java_com_rarnu_sample_NativeSample_hello(JNIEnv* env, jclass obj, jstring text) { return text; }
然後就是編譯,不得不說,C++ 挺麻煩的 :(
#!/bin/sh JNIPATH=${JAVA_HOME}/include JNIPLATFORM="linux" PLATFORM=`uname -s` SURFIX="so" if [ "$PLATFORM" = "Darwin" ]; then JNIPLATFORM="darwin" SURFIX="dylib" fi echo $JNIPATH echo $JNIPLATFORM g++ main.cpp \ -I${JNIPATH} \ -I${JNIPATH}/${JNIPLATFORM} \ -shared -fpic \ -o libSample.${SURFIX} \ -D JNI
好了,現在我們得到了一個libSample.dylib
,可以試著用一下了,此處需要注意的是,編譯指令碼相容了 Mac 和 Linux,如果在 Linux 下執行,那麼會得到libSample.so
,它們在使用上是一致的。
然後把這個 dylib 放到專案的根目錄下,寫一個呼叫的程式碼來使用它:
package com.rarnu.sample object NativeSample { init { System.loadLibrary("Sample") } external fun hello(text: String): String }
get("/hello") { val txt = call.parameters["txt"] ?: "" call.respondText { NativeSample.hello(txt) } }
完成後執行專案,就可以通過請求http://localhost:8080/hello?txt=rarnu
來看到效果了。
下面來填個坑,我們在 IDE 裡執行專案自然是沒問題的,但是以distribution
方式釋出到線上後,卻發現libSample.dylib
或libSample.so
丟失了,它並沒有被打包到釋出包內。要解決這一問題,我們必須修改 Gradle 指令碼:
distTar { into("${project.name}-${project.version}") { from '.' include 'lib*.*' } } distZip { into("${project.name}-${project.version}") { from '.' include 'lib*.*' } }
在build.gradle
最後加上這些,然後再進行gradle build
操作,就可以正常的把 JNI 庫打進發布包了。