JNI基本使用二
Posted zhujm320
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JNI基本使用二相关的知识,希望对你有一定的参考价值。
介绍
接着上一篇博文本次主要介绍JNI的动态注册,以及在JNI中如何开线程。JNI的基础部分,请详细参考JNI的基本使用一
动态注册
与静态注册相比,有如下优缺点:
优点:
1. native中函数名简洁, 方便记忆
2. 需要使用时,需要在 JNI_OnLoad() 进行注册
缺点:
1. native方法需要手动进行注册
工程中使用
对上一篇博文的工程进行改造,将静态注册改成动态注册。在JNI_OnLoad中采用 RegisterNatives 对JNI函数进行注册。
JNINativeMethod 说明
JNINativeMethod 动态注册使用的结构体,描述了函数的参数和返回值 。
typedef struct
char *name;
char *signature;
void *fnPtr;
JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值。
第三个变量fnPtr是函数指针,指向C函数。
动态注册步骤
1. 将java中的native函数名和jni中的实现通过进行一一绑定,填充JNINativeMethod结构体
2. 通过env->RegisterNatives进行注册
3. 在JNI_OnLoad中调用注册函数
代码如下:
填充JNINativeMethod结构体
//java文件路径
static const char *classPathName = "com/stx/jni/JNITest";
static JNINativeMethod methods[] =
"javaCallNative", "()V", (void *)(javaCallNative) ,
"javaCallNative1", "(I)V", (void *)(javaCallNative1) ,
"javaCallNative2", "(J)V", (void *)(javaCallNative2) ,
"javaCallNative3", "(F)V", (void *)(javaCallNative3) ,
"javaCallNative4", "([B)V", (void *)(javaCallNative4) ,
"javaCallNative5", "(Ljava/lang/String;)V", (void *)(javaCallNative5) ,
"nativeCallJavaTest", "()V", (void *)(nativeCallJavaTest)
;
实现注册函数
int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL)
return JNI_FALSE;
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
printf("register nativers error\\n");
return JNI_FALSE;
return JNI_TRUE;
int registerNatives(JNIEnv* env)
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0])))
return JNI_FALSE;
return JNI_TRUE;
在JNI_OnLoad中进行调用
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
printf("JNI_OnLoad....\\n");
JNIEnv* env = NULL;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
return -1;
if (env == NULL)
return -2;
int result = registerNatives(env);
if (result == 1)
printf("JNI Register success...\\n");
else
printf("JNI Register faild...\\n");
return JNI_VERSION_1_4;
在JNI中启动线程
如何创建线程
JNI中开启线程, 需要根据不同的操作系统,然后根据操作系统的API进行创建线程。下面以windows操作系统为例子。
windows 创建线程API
HANDLE
WINAPI
CreateThread(
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ __drv_aliasesMem LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
参数说明:
第一个参数 lpThreadAttributes
表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。
第二个参数 dwStackSize
表示线程栈空间大小。传入0表示使用默认大小(1MB)。
第三个参数 lpStartAddress
表示新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。
第四个参数 lpParameter
是传给线程函数的参数。
第五个参数 dwCreationFlags
指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
第六个参数 lpThreadId
将返回线程的ID号,传入NULL表示不需要返回该线程ID号
返回值: 返回线程句柄,NULL失败, 其他成功
创建线程代码:
void createJniThread(JNIEnv *env, jobject jObj)
printf("createJniThread---->\\n");
threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, &threadid);
如何在线程中获取JNIEnv
JNIEnv 是和线程有关的变量,JVM是和进程有关的变量。所以不同的线程,都有不同的JNIEnv对象,那么在新的线程中需要通过JVM来重新获取JNIEnv对象。 注意: 需要在JNI_OnLoad中将JVM变量保存下来,然后在其他线程中使用。
获取JNIEnv方法
jint AttachCurrentThread(void **penv, void *args);
释放资源
jint DetachCurrentThread()
代码如下:
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
printf("JNI_OnLoad....\\n");
JNIEnv* env = NULL;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
return -1;
if (env == NULL)
return -2;
int result = registerNatives(env);
if (result == 1)
printf("JNI Register success...\\n");
else
printf("JNI Register faild...\\n");
pJVM = vm; //保存JVM结构体
return JNI_VERSION_1_4;
/*
*
*线程执行函数
*/
void MyThread()
printf("this is new thread begin, thread id: %d \\n", threadid);
for (int i = 100; i < 110; i++)
printf("%d\\n", i);
if (pJVM != NULL)
printf("pJVM is not null, begin get jnienv...\\n");
JNIEnv* pEnv = NULL;
pJVM->AttachCurrentThread((void**)&pEnv, NULL);
if (pEnv != NULL)
printf("get jinenv success...\\n");
else
printf("get jinenv faild...\\n");
pJVM->DetachCurrentThread();
else
printf("pJVM is null...\\n");
printf("this is new thread end, thread id: %d \\n", threadid);
测试代码
在java中调用jni函数,并开启jni线程
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();
jniTest.createJniThread();
try
Thread.sleep(500);
catch (InterruptedException e)
e.printStackTrace();
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();
public native void createJniThread();
/**
* 提供给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);
测试结果
com.stx.jni.JNITest
==================
curDir D:\\Project\\java\\JNIDemo
this is java test print...
arg1: 10
================arg2 begin=============
0
1
2
3
4
5
================arg2 end=============
arg3: C++ Char
JNI_OnLoad....
JNI Register success...
javaCallNative---->
javaCallNative1---->, arg: 1
javaCallNative2---->, arg: 2
javaCallNative3---->, arg: 3.000000
javaCallNative4---->
1
2
3
4
5
6
javaCallNative5---->
csArgs: Hello World
nativeCallJavaTest---->
createJniThread---->
this is new thread begin, thread id: 13736
100
101
102
103
104
105
106
107
108
109
pJVM is not null, begin get jnienv...
get jinenv success...
this is new thread end, thread id: 13736
以上是关于JNI基本使用二的主要内容,如果未能解决你的问题,请参考以下文章
如何在 JNI 中将 char[] 转换为 ByteBuffer?