NDK Mapping 发布啦

Posted by rarnu on 03-12,2017

首先感谢一下医生,若是没有你催命般的催稿,还真就没有这篇了。作为催我的代价,请客可乐是没跑的了:)


首先,给出章鱼猫地址: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 方法,却花费了如此多的代码。那么再设想一下如果是操作 ListMap 或是其他复杂类型呢?几十行代码都不一定做得下来。而这正是 NDK Mapping 诞生的初衷,即帮助开发者完成类的映射


来个具体的实例看看效果吧,要特别说明的是,NDK Mapping 接受的映射类文件必须是 Kotlindata 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 来操作这样的类需要多少代码,你是否还记得 ListAdd 方法签名是什么样的?当然现在你说不记得也没关系了,在 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。有了这两个方法,就可以实现映射。而在实际开发中,基本上也只需要调用这两个方法,其他的一切操作,都是与平台和语言本身相关的了。


下面是映射关系表,参考这个表,可以知道在生成代码时的规则。

KotlinC++PascalJNI
IntintIntegerjint
Byteunsigned charBytejbyte
ShortshortShortIntjshort
Longlong longInt64jlong
FloatfloatExtendedjfloat
DoubledoubleDoublejdouble
BooleanboolBooleanjboolean
CharcharCharjchar
StringstringStringjstring
ListlistFPGListjobject
MapmapFPGMapjobject
SetsetFPGListjobject
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 内找到下载地址。