JNI的基本使用一

Posted zhujm320

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JNI的基本使用一相关的知识,希望对你有一定的参考价值。

介绍

       JNI即Java Native Interface的简称,java本地方法接口,通过JNI Java可以和C相互调用。Java语言也是通过JNI接口来调用系统的功能,只不过JNI的实现部分在JDK中,这样可以增加Java的功能。同样用户程序也可以通过实现JNI接口来调用本地方法。如: 如: windows和linux上Java程序需要调用外设驱动,就需要使用JNI;android上NDK也是通过JNI进行调用。本文主要讲解在windows上java通过jni调用本地方法,测试效果和在android上通过java调用so一样(JNI的规则一致的),为了方便点采用windows来实践。

准备工作:

  1. vs2015

  2. IntelliJ

    本次通过vs2015编写dll,然后通过java进行调用。

数据类型

基本数据类型

Java类型JNI类型说明
booleanjbooleantypedef unsigned char jboolean;
bytejbytetypedef signed char jbyte;
charjchartypedef unsigned short jchar;
shortjshorttypedef short jshort;
intjinttypedef long jint;
longjlongtypedef __int64 jlong;
floatjfloattypedef float jfloat;
doublejdoubletypedef double jdouble;
voidvoidvoid

从上可以看出jni中的基本类型,都是由其他类型定义而来

引用类型

  • jobject

    • jclass (java.lang.Class objects)

    • jstring (java.lang.String objects)

    • jarray (arrays)

      • jobjectArray (object arrays)

      • jbooleanArray (boolean arrays)

      • jbyteArray (byte arrays)

      • jcharArray (char arrays)

      • jshortArray (short arrays)

      • jintArray (int arrays)

      • jlongArray (long arrays)

      • jfloatArray (float arrays)

      • jdoubleArray (double arrays)

    • jthrowable (java.lang.Throwable objects)

签名

签名Java类型
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
L fully-class;  fully-class
[typetype[]

例子:

void test(int arg1, byte arg2, char arg3, String []arg4);

方法签名: (IBC[Ljava/lang/String;)V

类属性映射

JavaJni
MethodjmethodID
FieldjfieldID

Java调用JNI准备

  1. 需要调用使用jni的java文件需要静态加载动态库

static {
        System.loadLibrary("JNIDll");
}

2.声明native方法,native方法与c层实现的代码一一对映

 public native void javaCallNative();
    public native void javaCallNative1(int arg);
    public native void javaCallNative2(long arg);
    public native void javaCallNative3(float arg);
    public native void javaCallNative4(byte[] arg);
    public native void javaCallNative5(String arg);
    /**
     * 通过该接口c层回调java接口
     */
    public native void nativeCallJavaTest();

Java测试代码如下:

package com.stx.jni;

import java.io.File;

public class JNITest {

    static {
        System.loadLibrary("JNIDll");
    }

    public static void main(String[] args) {
        System.out.println("==================");
        File file = new File("");
        System.out.println("curDir " + file.getAbsolutePath());
        JNITest jniTest = new JNITest();
        jniTest.javaCallNative();

        jniTest.javaCallNative1(1);
        jniTest.javaCallNative2(2);
        jniTest.javaCallNative3(3.0f);
        byte[] byteTest = new byte[]{1, 2, 3, 4, 5, 6};
        jniTest.javaCallNative4(byteTest);
        String strTest = "Hello World";
        jniTest.javaCallNative5(strTest);
        jniTest.nativeCallJavaTest();
    }

    public native void javaCallNative();

    public native void javaCallNative1(int arg);
    public native void javaCallNative2(long arg);
    public native void javaCallNative3(float arg);
    public native void javaCallNative4(byte[] arg);
    public native void javaCallNative5(String arg);
    /**
     * 通过该接口c层回调java接口
     */
    public native void nativeCallJavaTest();


    /**
     * 提供给JNI调用
     * @param arg1
     * @param arg2
     * @param arg3
     */
    public void test(int arg1, byte[] arg2, String arg3){
        System.out.println("this is java test print...");
        System.out.println("arg1: " + arg1);
        System.out.println("================arg2 begin=============");
        for (byte b : arg2){
            System.out.println(""+b);
        }
        System.out.println("================arg2 end=============");
        System.out.println("arg3: " + arg3);
    }
    
}

C代码

工程准备

  1. 新建一个vs2015工程,选择dll

    文件->新建项目

点击确定,然后点击下一步,选择建dll工程

  1. 点击完成,工程就建好了。

  2. 将jni.h和jni_md.h拷贝到工程目录中

    C:\\Program Files\\Java\\jdk1.8.0_231\\include\\jni.h

    C:\\Program Files\\Java\\jdk1.8.0_231\\include\\win32\\jni_md.h

    并将其添加到工程中

  3. 新建jni实现文件 JNIDll.h和JNIDll.cpp

    本次实现为静态注册的方式,对静态注册说明一下, jni中的函数如何和java中的函数进行绑定
    通用格式如下, 如果包名为a.b.c, java文件名为d, 函数名为f,那么jni中的对应的函数名为:Java_a_b_c_d_f
    以测试工程包为例:
     

    javajni
    javaCallNative
    Java_com_stx_jni_JNITest_javaCallNative

     
  4. 代码如下:

    头文件

    #pragma once
    #include	"jni.h"
    
    #ifndef _Included_JNIDLL
    #define _Included_JNIDLL
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    
    	JNIEXPORT void JNICALL Java_com_stx_jni_JNITest_javaCallNative
    	(JNIEnv *, jobject);
    	JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative1
    	(JNIEnv *, jobject, jint);
    	JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative2
    	(JNIEnv *, jobject, jlong);
    	JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative3
    	(JNIEnv *, jobject, jfloat);
    	JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative4
    	(JNIEnv *, jobject, jbyteArray);
    	JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative5
    	(JNIEnv *, jobject, jstring);
    
    	JNIEXPORT void JNICALL Java_com_stx_jni_JNITest_nativeCallJavaTest
    	(JNIEnv *, jobject);
    
    
    	char* jstringtochar(JNIEnv *env, jstring jsStr);
    	jstring chartojstring(JNIEnv* env, char* csStr);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    
    


    c定义

    // JNIDll.cpp : 定义 DLL 应用程序的导出函数。
    //
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "jni.h"
    #include "JNIDll.h"
    
    
    
    JNIEXPORT void JNICALL Java_com_stx_jni_JNITest_javaCallNative
    (JNIEnv * env, jobject jObj) {
    	printf("Java_com_stx_jni_JNITest_javaCallNative---->\\n");
    }
    
    JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative1
    (JNIEnv * env, jobject jObj, jint arg) {
    	printf("Java_com_stx_jni_JNITest_javaCallNative1---->, arg: %d\\n", arg);
    	return 0;
    }
    
    
    JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative2
    (JNIEnv *env, jobject jObj, jlong arg) {
    	printf("Java_com_stx_jni_JNITest_javaCallNative2---->, arg: %d\\n", arg);
    	return 0;
    }
    
    JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative3
    (JNIEnv *env, jobject jObj, jfloat arg){
    	printf("Java_com_stx_jni_JNITest_javaCallNativ3---->, arg: %f\\n", arg);
    	return 0;
    }
    
    
    JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative4
    (JNIEnv *env, jobject jObj, jbyteArray arg) {
    
    	printf("Java_com_stx_jni_JNITest_javaCallNative4---->\\n");
    	int len = env->GetArrayLength(arg);
    	jbyte * pArray = env->GetByteArrayElements(arg, JNI_FALSE);
    	for (int i = 0; i < len; i++) {
    		printf("%d\\n", pArray[i]);
    	}
    	env->ReleaseByteArrayElements(arg, pArray, JNI_FALSE);
    	return 0;
    }
    
    
    JNIEXPORT int JNICALL Java_com_stx_jni_JNITest_javaCallNative5
    (JNIEnv *env, jobject jObj, jstring arg) {
    	printf("Java_com_stx_jni_JNITest_javaCallNative5---->\\n");
    	const char* csArgs = env->GetStringUTFChars(arg, false);
    	printf("csArgs: %s\\n", csArgs);
    	env->ReleaseStringUTFChars(arg, csArgs);
    	return 0;
    }
    
    
    
    JNIEXPORT void JNICALL Java_com_stx_jni_JNITest_nativeCallJavaTest
    (JNIEnv *env, jobject jObj) {
    	printf("Java_com_stx_jni_JNITest_nativeCallJavaTest---->\\n");
    	jclass clazz = env->FindClass("com/stx/jni/JNITest");
    	if (clazz == NULL)
    		clazz = env->GetObjectClass(jObj);
    
    	if (clazz != NULL) {
    		jmethodID method = env->GetMethodID(clazz, "test", "(I[BLjava/lang/String;)V");
    		if(method != NULL){
    			int arg1 = 10;
    			jbyte* pByte = new jbyte[6];
    			for (int i = 0; i < 6; i++) {
    				pByte[i] = i;
    			}
    			jbyteArray arg2 = env->NewByteArray(6);
    			env->SetByteArrayRegion(arg2, 0, 6, pByte);
    
    			char* pCsStr = "C++ Char";
    			jstring arg3 = chartojstring(env, pCsStr);
    			env->CallVoidMethod(jObj, method, arg1, arg2, arg3);
    		}
    		else {
    			printf("find method test faild...");
    		}
    	}
    	else {
    		printf("find class faild...");
    	}
    }
    
    
    char* jstringtochar(JNIEnv *env, jstring jsStr) {
    	char* rtn = NULL;
    	jclass clsstring = env->FindClass("java/lang/String");
    	jstring strencode = env->NewStringUTF("utf-8");
    	jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    	jbyteArray barr = (jbyteArray)env->CallObjectMethod(jsStr, mid, strencode);
    	jsize alen = env->GetArrayLength(barr);
    	jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    	if (alen > 0){
    		rtn = (char*)malloc(alen + 1);
    		memcpy(rtn, ba, alen);
    		rtn[alen] = 0;
    	}
    	env->ReleaseByteArrayElements(barr, ba, 0);
    	return rtn;
    }
    
    jstring chartojstring(JNIEnv* env, char* csStr) {
    	jclass strClass = env->FindClass("Ljava/lang/String;");
    	jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    	jbyteArray bytes = env->NewByteArray(strlen(csStr));
    	env->SetByteArrayRegion(bytes, 0, strlen(csStr), (jbyte*)csStr);
    	jstring encoding = env->NewStringUTF("utf-8");
    	return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
    }
    
    


    以上代码都可以编译运行通过。

         win32源码下载

 

 

以上是关于JNI的基本使用一的主要内容,如果未能解决你的问题,请参考以下文章

动态SQL基础概念复习(Javaweb作业5)

JNI基本使用二

JNI基本使用二

JNI基本使用二

jni使用javap查看java类方法签名

JNI的基本使用一