在 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
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 库打进发布包了。