• JNI的第2种写法:本地方法注册


    声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635

    孔乙己说,茴香豆的茴有四种写法,今天谈谈JNI的第2种写法:本地方法注册。

    这种写法的好处是不需要使用javah生成一个C++头文件,也不必使用javah自动生成的长长的C++函数名,往往在native函数很多的情况下,扩展比较灵活。
    之前的笔者写的文章中介绍的是函数名映射的方法,今天介绍的是采用注册本地方法的方式 。本质都是建立起Java层native函数和C层函数的映射关系。
    C++代码部分做了一些额外的测试操作,这些测试操作意在表明Java堆和C本地堆之间的关系。

    测试类

    package com.testjnitype2;
    
    public class TestJNIType2
    {
        private static NativeUtils nativeUtils = new NativeUtils();
    
        static
        {
            System.load("D:\test\src\com\testjnitype2\lib_testjni_Type2_amd64.dll");
        }
    
        public static void main(String[] args)
        {
            byte[] dataSharedByJavaC = new byte[10];
            System.out.println("JNIType2 before-->dataSharedByJavaC=" + dataSharedByJavaC);
            for (int i = 0; i < 10; i++)
            {
                System.out.print(dataSharedByJavaC[i] + " ");
            }
            System.out.println("
    ----------");
            nativeUtils.tranferBytes(dataSharedByJavaC);
            System.out.println("JNIType2 after-->dataSharedByJavaC=" + dataSharedByJavaC);
            for (int i = 0; i < 10; i++)
            {
                System.out.print(dataSharedByJavaC[i] + " ");
            }
        }
    }
    
    

    java native方法定义类

    package com.testjnitype2;
    
    public class NativeUtils
    {
        public native void tranferBytes(byte[] dataSharedByJavaC);
    }
    

    C++实现类

    #include "jni.h"
    #include "stdio.h"
    
    static const char *classPath = "com/testjnitype2/NativeUtils";
    
    int FAILURE = -1;
    
    int SUCESS = 0;
    
    JNIEXPORT void JNICALL tranfer_bytes(JNIEnv *jniEnv, jobject obj, jbyteArray dataSharedByJavaC);
    
    
    //step1-1:建立java方法与native 方法的映射关系
    //JNINativeMethod定义在jni.h中,定义如下
    /**
    typedef struct {
        char *name;
        char *signature;
        void *fnPtr;
    } JNINativeMethod;
    */
    static const JNINativeMethod methods[] =
    {
        /*
        tranferBytes为java层定义的方法,tranfer_bytes为native层定义的方法,此处就不需要写成Java_com_testjnitype2_NativeUtils_tranferBytes这种格式了
        */
        {"tranferBytes", "([B)V", (void*)tranfer_bytes}
    };
    
    //step 1-2:实现与java方法映射到的本地方法
    JNIEXPORT void JNICALL tranfer_bytes(JNIEnv *jniEnv, jobject obj, jbyteArray dataSharedByJavaC)
    {
        printf("
    ----------");
        //data在native层的地址
    	printf("
    dataSharedByJavaC addr = %x, dataSharedByJavaC and dataSharedByJavaC addr transfered from java parsed by native is random:
    ", dataSharedByJavaC);
    	for (int i=0; i<10; i++)
    	{
    		printf("%d ", dataSharedByJavaC[i]);
    	}
    
        //data在native层解析后指向java data后的地址
    	jbyte* p = jniEnv->GetByteArrayElements(dataSharedByJavaC, NULL);
    	printf("
    ----------");
    	printf("
    p addr = %x, make native p point to java heap, now native data pointed by p is definitized, in other wolds, it's java heap data:
    ", p);
        for (int i=0; i<10; i++)
        {
        	printf("%d ", p[i]);
        }
    
        //重新赋值,也就是更改Java heap中的数据
    	for (int i = 0; i < 10; i++)
    	{
    	    p[i] = i * i + 1;
    	}
    
    	jniEnv->ReleaseByteArrayElements(dataSharedByJavaC, p, 0);
        return;
    }
    
    //step2:在jvm中注册映射关系
    int registerNativeMethods(JNIEnv *env, const char *classPath)
    {
        printf("begin register native methods");
        jclass clazz = env->FindClass(classPath);
        if (!clazz)
        {
            return FAILURE;
        }
        if (env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod)) != JNI_OK)
        {
            return FAILURE;
        }
        return SUCESS;
    }
    
    /**
    step3:实现jni.h中声明的方法JNI_OnLoad,并调用step 2中的注册方法
    该方法在程序启动时,Java加载本地库的时被调用,比如System.load("xxx");
    */
    JNIEXPORT jint JNI_OnLoad(JavaVM *jvm, void* reserved)
    {
        printf("JNI OnLoad");
        JNIEnv *env;
        if (jvm->GetEnv((void**)&env, JNI_VERSION_1_8) != JNI_OK)
        {
            printf("get env error");
            return -1;
        }
        registerNativeMethods(env, classPath);
        return JNI_VERSION_1_8;
    }
    
    /**
    实现jni.h中声明的方法
    */
    JNIEXPORT void JNI_OnUnload(JavaVM* jvm, void* reserved)
    {
        JNIEnv *env;
        if (jvm->GetEnv((void**)&env, JNI_VERSION_1_8) != JNI_OK)
        {
            printf("get env error");
        }
    }
    
    

    C代码编译方法
    d:VS2010Microsoft Visual Studio 10.0VC>cl /LD D: estsrccom estjnitype2 estjnitype2.cpp -o D: estsrccom estjnitype2lib_testjni_Type2_amd64.dll

    编译完后目录大概如下图。
    这里写图片描述
    执行结果如下

    JNIType2 before-->dataSharedByJavaC=[B@78308db1
    0 0 0 0 0 0 0 0 0 0 
    ----------
    JNIType2 after-->dataSharedByJavaC=[B@78308db1
    1 2 5 10 17 26 37 50 65 82 
    ----------
    JNI OnLoad
    ----------
    begin register native methods
    ----------
    dataSharedByJavaC addr = 2c5f470, dataSharedByJavaC and dataSharedByJavaC addr transfered from java parsed by native is random:
    240 96 213 106 7 0 0 0 88 89 
    ----------
    p addr = 1dd95e70, make native p point to java heap, now native data pointed by p is definitized, in other wolds, it's java heap data:
    0 0 0 0 0 0 0 0 0 0
    

    执行结果暗含了很多信息,比如,输出的顺序、打印的数组值为什么会是上面的样子?这个会做个专题进行讨论。
    再说说jni的使用方法吧,其实,本地方法注册法和函数名映射法可以混合使用的。
    顺便说一句,netty集成平台动态库的方法就是用的这种。
    以上。

  • 相关阅读:
    数据结构与算法分析-Code Blocks中出现的找不到头文件的问题
    数据结构与算法分析-用C语言实现栈(数组方式)
    数据结构与算法分析-用C语言实现栈(链表方式)
    数据结构与算法分析-用C语言实现单链表
    C语言经典算法100例-结束语
    C++ Primer 7.33 练习编写成员函数
    C语言经典算法100例-073-链表逆序插入节点
    C语言经典算法100例-072-创建一个链表
    LintCode-编辑距离
    LintCode-乘积最大子序列
  • 原文地址:https://www.cnblogs.com/xsl-thumb-rfcs/p/9941600.html
Copyright © 2020-2023  润新知