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基本使用二的主要内容,如果未能解决你的问题,请参考以下文章