JNI与NDK开发(三)—— Java调用C的推荐方法

在上一篇博客 JNI与NDK开发(二)——JNI基本概念 与 Java对C或C++函数的简单调用 我们介绍了 Java调用C的基本方法,这种方法虽然足够简单,但是写法太死板,多个Native方法时,大量基础代码重复。
Android 和 JNI推荐我们使用的方法是:
主要步骤:
1、在Java层定义,native关键字的方法;在这里插入图片描述
2、通过RegisterNative手工映射Java的native方法和C/C++函数;
在这里插入图片描述
上图是cpp写的对应Java的Native函数 和 手工映射Java与C/C++的代码。下面具体介绍一下:

①、定义宏,将Native的class文件路径,用JNI_CLASS_PATH替代,便于后面调用和维护修改;

②、用C/C++代码,实现Java中的Native方法对应调用的具体业务逻辑。这里除了函数名称可以随便写(是的随便写,只要符合C/C++的函数名称规则就好),其他跟第一种JNI调用方法没有任何区别。
注意:C/C++的函数,必须要写到④和⑧前面,否则会报函数未定义;

③、函数名称,跟Java中Native没有半毛钱关系,跟包名类名也无关,随便写的。Java中Native方法和这里的函数,是通过④和⑧这两块代码,手动映射建立联系。这样可以更灵活,不用受制于JNI原有的命名规则的限制;

④、匹配Java的Native方法名和C/C++的函数名称,并通过Signature描述符,表示函数参数的结构和返回类型;

⑤、Java的Native方法名;

⑥、Signature描述符,表示函数参数的结构和返回类型(后续会详细讲,怎么写Signature,很简单,就是标识符);
⑦、C/C++的函数名称;

⑧、真正进行映射的代码,前面步骤基本都是为此准备参数。这里基本是固定写法,只需要写一次后续拷贝就好。(如果每次都要自己写,那就没有什么优势可言了);

    JNIEnv *env = NULL;
    vm->GetEnv((void**)&env, JNI_VERSION_1_6);

通过JavaVM获取JNIEnv 对象,JNIEnv 是负责进行Java与C/C++方法函数映射,这一点上一篇讲过了。GetEnv的第一个参数,是输出,第二个是使用JNI的版本。

env->RegisterNatives(classz,g_methods,sizeof(g_methods)/sizeof(g_methods[0]))

获取到JNIEnv 对象 env,可以调用其方法RegisterNatives,进行Native方法和C/C++函数进行关联。
classz: 表示Java中native方法所在的class对象;
g_methods:组织Java的native方法 与 C/C++函数进行匹配;
sizeof(g_methods)/sizeof(g_methods[0]):计算g_methods中匹配的个数;
注意:JNI_OnLoad 这个是固定的函数名,不能改变。否则编译不报错,但运行会崩溃。

⑨、JNI_VERSION_1_6 这里表示使用JNI的那个版本。JNI_VERSION_1_6 应该是最新版本吧,JNI_VERSION_1_4这里也是可以用的,应该是向下兼容的。(这里有点不太清楚,没看到啥资料讲。若是你知道,请留言告诉我,谢谢啦!)

以下便于拷贝,附代码:

#include <jni.h>
#include <string>
#define JNI_CLASS_PATH "com/example/jnifrst/jnifrst/MainActivity"

extern "C" JNIEXPORT jstring JNICALL
my_jni_recommend(
        JNIEnv* env,
        jobject instance){
    return env->NewStringUTF("This is the recommended method -- One!");
}

extern "C" JNIEXPORT jstring JNICALL
my_jni_recommend_two(
        JNIEnv* env,
        jobject instance){
    return env->NewStringUTF("This is the recommended method -- Two!");
}

static JNINativeMethod g_methods[] = {
    {"jniRecommend","()Ljava/lang/String;",(void*)my_jni_recommend},
    {"jniRecommend2","()Ljava/lang/String;",(void*)my_jni_recommend_two},
};

jint JNI_OnLoad(JavaVM *vm,void *reserved){
    JNIEnv *env = NULL;
    vm->GetEnv((void**)&env, JNI_VERSION_1_6);
    jclass classz = env->FindClass(JNI_CLASS_PATH);
    env->RegisterNatives(classz,g_methods,sizeof(g_methods)/sizeof(g_methods[0]));
    return JNI_VERSION_1_6;
}

以上就是官方推荐的JNI的调用方式。我看很多Android源码,基本都是基于这种方式进行JNI的调用。其中,有很多是规定写法。这些不是我创造的,我只是把我知道的,尽量表达清楚(整天不说话,文章也不会写,慢慢会不会变成自闭?)。
真的,多看源码好处多多。不过看多了,就开始怀疑人生了!
下一篇,我预计把这篇中用到的 Signature 说清楚,其实这个也很简单。固定的格式,固定的标签表示方式。期待,这几天系统无bug。解系统bug,真的力不从心。我原本只是写应用的而已…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值