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

有时候,我们需要在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方法笔记少,当方法数量增多时,需要重新测试才能知道结果了.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值