Android JNI初步☞Java方法和native方法关联

Android JNI初步☞Java方法和native方法关联有时候,我们需要在Java代码中调用c/c++代码,大致的格式是这样的:publicnativevoidhelloworld();但是但是如何让这个java方法和c/c++中的方法对应起来呢,也既是当我们在java中调用被声明为native的方法时,自动调用c/++中的方法.这种对应关系,有两种方式来实现:静态注册和动态注册.

有时候,我们需要在Java代码中调用c/c++代码,大致的格式是这样的:

public native void helloworld();

但是但是如何让这个java方法和c/c++中的方法对应起来呢,也既是当我们在java中调用被声明为native的方法时,自动调用c/++中的方法.这种对应关系,有两种方式来实现:静态注册和动态注册.下面我们来了解一下:

静态注册

所谓静态注册,通常步骤大致如下:

1. 编写需要的Java方法,并编译出class文件:

public class StaticRegisterNativeMethod { 
   
    static {
        System.loadLibrary("RegisterMethodTest");
    }

    public native String hello();
}

2. 通过javah工具生成对应的.h头文件:

javah -o xxx.h packagename.DynamicRegisterNativeMethod

头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_xiao_jnitest_StaticRegisterNativeMethod */

#ifndef _Included_com_xiao_jnitest_StaticRegisterNativeMethod
#define _Included_com_xiao_jnitest_StaticRegisterNativeMethod
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: com_xiao_jnitest_StaticRegisterNativeMethod * Method: hello * Signature: ()Ljava/lang/String; */
JNIEXPORT jstring JNICALL Java_com_xiao_jnitest_StaticRegisterNativeMethod_hello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

3. 在.c文件中编写我们的实现:

#include <string.h>
#include "StaticRegisterMethod.h"

/* * Class: com_xiao_jnitest_StaticRegisterNativeMethod * Method: hello * Signature: ()Ljava/lang/String; */
jstring Java_com_xiao_jnitest_StaticRegisterNativeMethod_hello
  (JNIEnv *env, jobject instance){
    return (*env)->NewStringUTF(env, "World from static register method!");
}

4. 之后,我们在Java文件中使用hello这个方法时,jvm会自动帮我们和c/c++中方法对应起来:

static register

总结:
然后我们就发现,写个native方法好麻烦啊,而且c/c++中对应的方法名十分不人性化,弊端大致有:

  • 声明native的java类,每个对应的class文件都要用javah命令生成头文件,好烦人.而且,以后修改了java中方法的定义,又要重新生成头文件.
  • javah生成的native函数名特别长,对人不友好
  • 初次调用native方法时,jvm要根据函数名字搜索对应的native方法建立关联关系,影响运行效率.

于是就出现了下面这种动态注册方案

动态注册

步骤如下:

1. 编写需要的Java方法:

public class DynamicRegisterNativeMethod { 
   
    static {
        System.loadLibrary("RegisterMethodTest");
    }

    public native String hello();
}

2. 在对应的c/c++文件中,编写native实现:

jstring DynamicRegisterNativeMethod_hello
  (JNIEnv *env, jobject instance)
{
    return (*env)->NewStringUTF(env, "World from dynamic register method!");
}

3. 通过JNINativeMethod来构建一个数组,声明java方法和native方法的对应关系:

static JNINativeMethod gMethods[] =
{
    {
        "hello", //Java中native函数的函数名
        "()Ljava/lang/String;", // Java中的native对应的native签名
        (void *)DynamicRegisterNativeMethod_hello //native 中的方法指针
    },
};

4. 在我们调用System.loadLibrary()方法时,native中的JNI_OnLoad方法会被调用,因此我们需要在这个方法中动态注册我们的jni方法:

int register_method(JNIEnv *env)
{
    jclass clazz = (*env)->FindClass(env, "com/xiao/jnitest/DynamicRegisterNativeMethod");
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env=NULL;
    jint result = -1;

    if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK){
        return -1;
    }
    assert(env != NULL);

    if(!register_method(env)){
        return -1;
    }

    result = JNI_VERSION_1_4;

    return result;
}

5. 最后,我们在Java文件中使用hello这个方法,结果就像下面这样:

dynamic register

结语

动态注册和静态注册大致就是这样.上面我们提到,静态注册在初次使用native方法时,需要建立native方法和java方法之间的关联,因此会必将耗时,那么我们来测试一下静态加载的状况:

MainActivity: Static register method cost time:14 //第一次
MainActivity: Static register method cost time:3
MainActivity: Static register method cost time:4
MainActivity: Static register method cost time:11 //第一次
MainActivity: Static register method cost time:3
MainActivity: Static register method cost time:3
MainActivity: Static register method cost time:11 //第一次
MainActivity: Static register method cost time:5
MainActivity: Static register method cost time:4

我们发现,第一次的确比较耗时,那么对比一下动态加载:

MainActivity: Dynamic register method cost time:11 //第一次
MainActivity: Dynamic register method cost time:3
MainActivity: Dynamic register method cost time:3
MainActivity: Dynamic register method cost time:11 //第一次
MainActivity: Dynamic register method cost time:5
MainActivity: Dynamic register method cost time:5
MainActivity: Dynamic register method cost time:10 //第一次
MainActivity: Dynamic register method cost time:3
MainActivity: Dynamic register method cost time:4

然后我们发现,第一次使用时的时间差异并不明显.不过这有一部分原因是我们的native方法笔记少,当方法数量增多时,需要重新测试才能知道结果了.

今天的文章Android JNI初步☞Java方法和native方法关联分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/29630.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注