JNI 原理与静态、动态注册

原文链接:https://blog.csdn.net/q610098308/article/details/79395232

1.JNI概述

Android系统按语言来划分的话由两个世界组成,分别是Java世界和Native世界。那为什么要这么划分呢?Android系统由Java写不好吗?除了性能的之外,最主要的原因就是在Java诞生之前,就有很多程序和库都是由Native语言写的,因此,重复利用这些Native语言编写的库是十分必要的,况且Native语言编写的库具有更好的性能。
这样就产生了一个问题,Java世界的代码要怎么使用Native世界的代码呢,这就需要一个桥梁来将它们连接在一起,而JNI就是这个桥梁。

通过JNI,Java世界的代码就可以访问Native世界的代码,同样的,Native世界的代码也可以访问Java世界的代码。
 
静态注册

原理:根据函数名来建立 java 方法与 JNI 函数的一一对应关系;

实现流程:

    编写 java 代码;
    利用 javah 指令生成对应的 .h 文件;
    对 .h 中的声明进行实现;

弊端:

    编写不方便,JNI 方法名字必须遵循规则且名字很长;
    编写过程步骤多,不方便;
    程序运行效率低,因为初次调用native函数时需要根据根据函数名在JNI层中搜索对应的本地函数,然后建立对应关系,这个过程比较耗时;

动态注册

原理:利用 RegisterNatives 方法来注册 java 方法与 JNI 函数的一一对应关系;

实现流程:

    利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系;
    实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册;
    调用 FindClass 方法,获取 java 对象;
    调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册;

优点:

    流程更加清晰可控;
    效率更高;

 
JNINativeMethod

在动态注册的过程中使用到了结构体 JNINativeMethod 用于记录 java 方法与 jni 函数的对应关系

结构体的第一个参数 name 是java 方法名;

第二个参数 signature 用于描述方法的参数与返回值;

第三个参数 fnPtr 是函数指针,指向 jni 函数;

其中,第二个参数 signature 使用字符串记录方法的参数与返回值,具体格式形如“()V”、“(II)V”,其中分为两部分,括号内表示的是参数,括号右侧表示的是返回值;
 

数据类型映射

2. 数组引用类型

如果是一维数组则遵循下表,如果是二维数组或更高维数组则对应的 native 类型为 jobjectArray,域描述符中使用 ‘[’ 的个数表示维数

3. 对象引用类型

对于其它引用类型,即 java 中的对象,其映射规则为

4. 对象数组引用类型

如果是一维数组则遵循下表,如果是二维数组或更高维数组则对应的 native 类型为 jobjectArray,域描述符中使用 ‘[’ 的个数表示维数

 
jni 函数默认参数

在 jni 函数中有两个默认参数

JNIEnv *env, jobject thiz

 其中 JNIEnv 指代的是当前 java 环境,可以利用 JNIEnv 可以操作 java 层代码;jobject 指代的是 jni 函数对应的 java native 方法的类实例,如果 java 方法是 static,则代表的是 class 对象;

例:

java

    package cn.com.jni.jniautoreg;
     
    public class TestJNI {
        public static native void  native_init();
        public static native String    stringFromJNI(String name,String address);
    }

 native

#include <jni.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <memory.h>
 
#define LOG_TAG "my-jni"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
using namespace std;
static const char *const kClassJniTest =
        "cn/com/jni/jniautoreg/TestJNI";
 
 
jstring JNI_stringFromJNI(JNIEnv *env, jobject obj, jstring name,jstring address) {
    LOGE("JAVA  call  JNI stringFromJNI");
    string hello = "Hello from JNI";
    return env->NewStringUTF(hello.c_str());
}
 
 
 
void JNI_native_init(JNIEnv *env, jobject thizz) {
    LOGE("  native_1init");
 
}
 
static const JNINativeMethod gMethods[] = {
        {
                "native_init",
                "()V",
                (void *) JNI_native_init
        },
 
       {
                "stringFromJNI",
                "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
                (void *) JNI_stringFromJNI
        },
};
 
static int registerNativeMethods(JNIEnv *env, const char *className, const JNINativeMethod *gMethod,
                                 int numMethods) {
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        LOGI(" JNI reg faild:%s",className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethod, numMethods) < 0) {
        LOGI(" JNI reg method failed:%s",gMethod->name);
        return JNI_FALSE;
    }
 
    return JNI_TRUE;
}
 
static int register_along_jni(JNIEnv *env) {
 
    return registerNativeMethods(env, kClassJniTest, gMethods,
                                 NELEM(gMethods));
}
 
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /* reserved */) {
 
    JNIEnv *env = NULL;
    jint result = -1;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        LOGE("ERROR: JNI version error");
 
        return JNI_ERR;
    }
    if (register_along_jni(env) == -1) {
        LOGE("ERROR:  JNI_OnLoad failed");
        return JNI_ERR;
    }
    result = JNI_VERSION_1_6;
    return result;
}

 
注意:
在JNI_OnLoad函数的结尾处,我们一定要有返回值,而且必须是JNI_VERSION_1_4 或 JNI_VERSION_1_6,也就是JNI的版本号,我们一定要返回正确的版本号,否则系统也是无法加载的。
————————————————
版权声明:本文为CSDN博主「恋恋西风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/q610098308/article/details/79395232

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值