jni不支持类型为void*,unsigned int*,....怎么办?

Vik*_*yan 4 java eclipse java-native-interface types

我有.so(共享库)用C++编写,让我们称之为function.so,其中我实现了不同的函数,这里是一些函数的列表:

1. unsigned long Initialize(void* userData);
2. unsigned long Uninitialize(void);
3. unsigned long DeviceOpen( unsigned long id, unsigned long* device);
4. unsigned long DeviceClose( unsigned long device );
Run Code Online (Sandbox Code Playgroud)

等等 ...

我想在我的java应用程序中使用这个库的(functional.so)功能.为此我在我的android应用程序项目文件夹中创建jni文件夹并放置文件:

  1. Android.mk

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE           := Test_library
    LOCAL_SRC_FILES        := Test_library.c
    ## Linking functionality library
    LOCAL_LDLIBS := -lfunctionality
    include $(BUILD_SHARED_LIBRARY)
    
    Run Code Online (Sandbox Code Playgroud)
  2. Test_library.c

    #include <string.h>
    #include <jni.h>
    #include "Test_library.h"
    
    jint Java_com_Dsm_Test_DsmLibraryTest_vtUninitialize(JNIEnv* env, jobject thiz) {
    return Uninitialize( );
    }
    
    jint Java_com_Dsm_Test_DsmLibraryTest_vtDeviceClose(JNIEnv* env, jobject thiz, jint hDevice) {
    return DeviceClose( hDevice );
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. Test_library.h

    其中A头文件初始化,未初始化,DeviceOpen,DeviceClose函数声明.

在此之后,我运行ndk-build并创建一个Test_library.so库并将其加载到我的java应用程序中并使用它们:

// Some code

public native int Uninitialize( );

public native int DeviceClose( int hDevice );

static {
    System.loadLibrary("Test_library");
}
Run Code Online (Sandbox Code Playgroud)

一切都运行良好.之后我想添加其他两个功能

1. unsigned long Initialize(void* userData);
2. unsigned long DeviceOpen( unsigned long id, unsigned long* device);
Run Code Online (Sandbox Code Playgroud)

`

现在问题:

  1. 我怎么能把这两个函数写成java中的native?因为java 中没有void*unsigned long*类型
  2. 我如何在Test_library.c中编写与jni.h中相同的函数,没有void**或unsigned long*类型

感谢帮助.

Ern*_*ill 8

您可以使用jlong将指针(或指针指针等)传递回Java.Java代码将无法将其用于任何事情,除了将其作为参数传递给您的其他方法之外; 但通常这就是你真正想要的.另一方面,如果您希望Initialize()使用Java设置数据进行调用,那么这void *是不合适的; 您需要使用Java类,并在JNI中使用反射来获取所需的信息.

好听的,你可以包装malloc()free():

jlong Java_c_utils_malloc(JNIEnv* env, jclass clazz, jint size) {
    return (jlong) malloc(size);
}

void Java_c_utils_free(JNIEnv* env, jclass clazz, jlong ptr) {
   free((void *) ptr);
}
Run Code Online (Sandbox Code Playgroud)

然后在Java中使用它们(无效!):

long ptr = utils.malloc(100);
// Store ptr for a while
utils.free(ptr);
Run Code Online (Sandbox Code Playgroud)

现在,如果我们将一些需要一块内存作为参数的其他函数包装起来,我们也可以将它们包装起来,让它们接受一个jlong参数,就像free()这样.Java变量ptr表示内存地址的事实在Java中是完全不透明的,但它仍然有用.

Java的窗口系统实现(即,AWT,SWT)使用同样的东西来将本机窗口小部件句柄与Java组件相关联.

现在,如果你希望你Initialize()能够从Java中获取有用的参数,那么a void *不会削减它.您需要编写自己的方法来接受Java对象作为参数; 这是允许您在Java中操作对象的唯一方法.

我不想在这里复制所有代码,但Sun的JNI教程就在这里.是关于调用Java对象的任意方法的部分(this对象或作为参数传递给方法的对象),是访问对象字段的类似部分.


pro*_*ron 6

你想要做的是:对于void*函数,使用直接字节缓冲区.在Java方面:

native long initialize(java.nio.ByteBuffer userData);
Run Code Online (Sandbox Code Playgroud)

调用方法时,请确保分配直接的ByteBuffer(请参阅java.nio.ByteBufferJNI nio用法):

ByteBuffer myData = ByteBuffer.allocateDirect(size);
long res = initialize(myData);
Run Code Online (Sandbox Code Playgroud)

在C方面,你这样做:

unsigned long res = Initialize(env->GetDirectBufferAddress(env, buffer));
return (jlong)res;
Run Code Online (Sandbox Code Playgroud)

您可以使用ByteBuffer方法从Java端的缓冲区读取和写入.

您也可以在C侧分配字节缓冲区env->NewDirectByteBuffer(env, ptr, size.

现在,对于第二个函数,我假设unsigned long*参数用于返回结果.您可以使用相同的方法(直接ByteBuffers),但我会推荐一个不需要为这么小的值分配缓冲区的方法.

在Java方面:

native long deviceOpen(long id, long[] device);
Run Code Online (Sandbox Code Playgroud)

在C方面:

unsigned long c_device;
unsigned long res = DeviceOpen((unsigned long)j_id, &c_device);
env->SetLongArrayRegion(env, j_device, 0, 1, &c_device);
return (jlong)res;
Run Code Online (Sandbox Code Playgroud)

然后从Java调用该方法:

long[] deviceOut = new long[1];
long res = deviceOpen(id, deviceOut);
long device = deviceOut[0];
Run Code Online (Sandbox Code Playgroud)

有关JNI的数组访问的更多信息,请参阅JNI数组操作