首先感谢一下医生,若是没有你催命般的催稿,还真就没有这篇了。作为催我的代价,请客可乐是没跑的了:)
首先,给出章鱼猫地址:rarnu/ndkmapping
NDK Mapping
的主要工作就是完成 class 从 JVM 层到 JNI 层的映射。通常情况下,当我们进行 JNI 开发时,无可避免的要进行类的传递操作,而 JNI 提供的 API 却让代码简单不起来,大量的容易出错的体力劳动也是这么来的。来看看以下的代码:
DemoInc *ret = NULL;
if (env && obj) {
ret = new DemoInc();
jclass cls = env->FindClass("com/sample/DemoInc");
jmethodID m = env->GetMethodID(cls, "getId", "()I");
ret->id = env->CallIntMethod(obj, m);
}
return ret;
大家都能读懂的吧?就是调用一下 JVM 层 DemoInc
类的 getId
方法,却花费了如此多的代码。那么再设想一下如果是操作 List
,Map
或是其他复杂类型呢?几十行代码都不一定做得下来。而这正是 NDK Mapping
诞生的初衷,即帮助开发者完成类的映射。
来个具体的实例看看效果吧,要特别说明的是,NDK Mapping
接受的映射类文件必须是 Kotlin
的 data class
,原因很简单,一方面是因为这样的 class 足够简单,方便解析,另一方面就是我懒。看看这样一个 class:
data class Demo(
var v1: Int, var v2: String, var v3: Context?,
var v4: IntArray?, var v5: List<String>?, var v6: Map<Int, View?>?
)
想一下用 JNI 来操作这样的类需要多少代码,你是否还记得 List
的 Add
方法签名是什么样的?当然现在你说不记得也没关系了,在 NDK Mapping
的帮助下,开发者不需要记忆任何与类操作有关的东西。
简单的看一下 NDK Mapping
的命令参数,当直接输入 ndkmapping
命令时,即可看到如下的参数提示:
ndkmapping <options> <Kotlin Class File Path>
options:
-l language (cpp, pas)
-b build option (mk, mksh)
-m max array size (must >= 0)
-o output path
-l
表示目标语言,目前可以生成 C++ 和 Pascal 的类映射,-b
表示生成 Makefile,-m
表示数组传参时,数组的最大下标,-o
表示生成的文件输出的位置,若是没有该目录,则会新建一个。当然在最后还得再跟上 Kotlin Class
的所在目录,ndkmapping
会自动的映射所有的 class
文件。
完整的命令如下:
$ ndkmapping -l cpp -b mksh -m 100 -o ./out/ ./kotlin/
$ cd out
$ ./build.sh
是的,你没有看错,生成的代码是可以直接编译的,并不需要再经过任何的修改,此时在 JNI 层的代码内,就有了一个与 JVM 层形态完全一样的类,可以直接操作。
而最关键的,是生成了两个方法:
static Demo* Demo::fromJObject(JNIEnv *env, jobject obj);
jobject Demo::toJObject(JNIEnv *env);
顾名思议也很清晰了,一个是将 jobject 所对应的类,翻译成 JNI 的类,而另一个,是将 JNI 的类翻译回 jobject。有了这两个方法,就可以实现映射。而在实际开发中,基本上也只需要调用这两个方法,其他的一切操作,都是与平台和语言本身相关的了。
下面是映射关系表,参考这个表,可以知道在生成代码时的规则。
Kotlin | C++ | Pascal | JNI |
---|---|---|---|
Int | int | Integer | jint |
Byte | unsigned char | Byte | jbyte |
Short | short | ShortInt | jshort |
Long | long long | Int64 | jlong |
Float | float | Extended | jfloat |
Double | double | Double | jdouble |
Boolean | bool | Boolean | jboolean |
Char | char | Char | jchar |
String | string | String | jstring |
List | list | FPGList | jobject |
Map | map | FPGMap | jobject |
Set | set | FPGList | jobject |
Array | [array] | [array] | jobjectArray |
对于一份生成好的代码来说,进行验证是有必要的。NDK Mapping
同样也提供了验证的能力。使用 ndktester
即可。
ndktester <options> <Kotlin Class File Path>
options:
-l language (java, kotlin)
-x exported JNI code language (cpp, pas)
-b build option (mk, mkshcp)
-p base package name
-c copy path
-o output path
参数基本上都与ndkmapping
类似,要额外选择验证代码的语言,和原始生成的代码语言,另外还需要用于 JVM 验证的包名,如果你需要在编译验证库后复制到其他项目中,可以使用-c
参数。
命令的样本如下:
ndktester -l kotlin -x cpp -b mkshcp -p com.sample.ndk -c ./jniLibs/ -o ./out/ ./kotlin/
此时就会生成用于 Kotlin 验证的代码,直接引入到一个项目即可。
保险起见,另外还提供了一份较为复杂的类的映射样例代码,可以从项目的 README 内找到下载地址。