jni不支持void *,unsigned int *等类型,该怎么办?

| 我有一个用C ++编写的.so(共享库),我们称它为functional.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 );
等等 ... 我想在我的Android Java应用程序中使用该库的(functionality.so)功能。为此,我在我的android应用程序项目文件夹中创建jni文件夹,并将文件放置在其中: 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)
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 );
}
Test_library.h 头文件,其中声明了Initialize,Uninitialize,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\");
}
一切运行正常。我要添加其他两个功能之后
1. unsigned long Initialize(void* userData);
2. unsigned long DeviceOpen( unsigned long id, unsigned long* device);
` 现在的问题: 我如何才能将这两个函数编写为Java本机?由于Java中没有void *或无符号long *类型 我如何在Test_library.c中编写与jni.h中相同的函数,没有void **或无符号long *类型 感谢帮助。     
已邀请:
        您可以使用
jlong
将指针(或指向该指针的指针,或其他)传递回Java。 Java代码除了将它作为参数传递给其他方法之一之外,将不能用于任何其他用途;但是通常这就是您真正想要的。另一方面,如果您希望使用Java设置的数据来调用ѭ6,,则
void *
是不合适的。您将需要使用Java类,并在JNI中使用反射来从中获取所需的信息。 粗略地讲,您可以将
malloc()
和ѭ9wrap包装起来:
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);
}
然后在Java中使用它们(无效!):
long ptr = utils.malloc(100);
// Store ptr for a while
utils.free(ptr);
现在,如果我们包装一些需要一块内存作为参数的其他函数,我们也可以包装它们,并让它们接受
jlong
参数,就像
free()
一样。 Java变量“ 14”表示一个内存地址这一事实在Java中是完全不透明的,但是仍然很有用。 Java的窗口系统实现(即AWT,SWT)使用相同的方式将本机窗口小部件句柄与Java组件相关联。 现在,如果您希望
Initialize()
能够从Java中获取有用的参数,那么
void *
就不会削减它。您需要编写方法以接受Java对象作为参数。这是允许您使用Java操作对象的唯一方法。 我不想在这里重复所有代码,但是Sun的JNI教程在这里。这是关于调用Java对象(arbitrary17对象,或作为参数传递给您的方法的任意方法)的部分,这与访问对象的字段类似。     
        您要做的是: 对于void *函数,请使用直接字节缓冲区。在Java方面:
native long initialize(java.nio.ByteBuffer userData);
调用该方法时,请确保分配一个直接的ByteBuffer(请参阅java.nio.ByteBuffer和JNI nio用法):
ByteBuffer myData = ByteBuffer.allocateDirect(size);
long res = initialize(myData);
在C端,您可以执行以下操作:
unsigned long res = Initialize(env->GetDirectBufferAddress(env, buffer));
return (jlong)res;
您可以使用ByteBuffer方法从Java端的缓冲区读取和写入。 您也可以用ѭ21with在C侧分配字节缓冲区。 现在,对于第二个函数,我假设使用unsigned long *参数返回一个结果。您可以使用相同的方法(直接ByteBuffers),但是我建议使用另一种方法,这种方法不必为这么小的值分配缓冲区。 在Java方面:
native long deviceOpen(long id, long[] device);
在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;
然后,您从Java调用方法:
long[] deviceOut = new long[1];
long res = deviceOpen(id, deviceOut);
long device = deviceOut[0];
有关从JNI访问数组的更多信息,请参见JNI数组操作。     
到目前为止,我认为@pron答案是最好的。 关于自动生成JNI代码,请考虑使用jnigen 它非常易于使用且功能强大。 获取Javadocs,然后查找NativeCodeGenerator类。 它在Java类型和本地CPP类型之间具有已应用的映射,例如String到char *,int []到int *,FloatBuffer到float *等。 GitHub上描述的Jnigen   jnigen是一个小型库,可以与libgdx一起使用,也可以不与libgdx一起使用,它允许C / C ++代码与Java源代码内联地编写。      jnigen有两个部分:         检查特定文件夹中的Java源文件,检测本机方法和附加的C ++实现,并吐出C ++源文件和标头,类似于使用JNI手动创建的文件。   为Ant构建脚本提供一个生成器,该脚本为每个平台构建本机源。    例: 这是您的Java本机方法,在方法声明之后,将所需的cpp代码定义为注释:
private static native ByteBuffer newDisposableByteBuffer (int numBytes); /*
   char* ptr = (char*)malloc(numBytes);
   return env->NewDirectByteBuffer(ptr, numBytes);
*/

private native static void copyJni (float[] src, Buffer dst, int numFloats, int offset); /*
   memcpy(dst, src + offset, numFloats << 2 );
*/
运行jnigen generator后,您将获得带有C代码和绑定的* .cpp文件。 * .h也会自动创建。 cpp将如下所示:
JNIEXPORT jobject JNICALL 
Java_com_badlogic_gdx_utils_BufferUtils_newDisposableByteBuffer
(JNIEnv* env, jclass clazz, jint numBytes) {
//@line:334
   char* ptr = (char*)malloc(numBytes);
   return env->NewDirectByteBuffer(ptr, numBytes);
}

JNIEXPORT void JNICALL 
Java_com_badlogic_gdx_utils_BufferUtils_copyJni___3FLjava_nio_Buffer_2II
(JNIEnv* env, jclass clazz, jfloatArray obj_src, jobject obj_dst, jint numFloats, jint offset) {
   unsigned char* dst = (unsigned char*)env->GetDirectBufferAddress(obj_dst);
   float* src = (float*)env->GetPrimitiveArrayCritical(obj_src, 0);

//@line:348
   memcpy(dst, src + offset, numFloats << 2 );

   env->ReleasePrimitiveArrayCritical(obj_src, src, 0);
}
使用jnigen,这就是您的方法的外观:
 /**
 * @param userData - a pointer. Assumed position() is Zero.
 **/
public native static long initialize(Buffer userData);/*
  return (jlong) Initialize( (void*) userData);
*/

public native static long uninitialize();/*
  return (jlong) Uninitialize();
*/

/**
 *  Assumptions : id points to a unique device
 * @param id - id
 * @param device - a long[] with length 1, to return device pointer.
 */
public native static long deviceOpen(long id, long[] device);/*
  return (jlong) DeviceOpen( (unsigned long) id, (unsigned long*) device);
*/ 

public native static long deviceClose(long device);/*
  return (jlong) DeviceClose( (unsigned long) device);
*/ 
    
        您是否考虑过为此使用自动包装器生成器?我已经使用SWIG几次,并取得了很大的成功。我之所以提及它,是因为它具有完善的模式来处理诸如,28ѭ之类的指针。 如果要包装的方法与上面列出的方法一样简单,则生成包装程序就像传递库的头文件一样容易。即使您有一些更复杂的方法(传递结构,将指针返回到内存等),SWIG也允许您根据需要将代码添加到生成的包装器中。 另一个优点是SWIG可以包装成多种语言,而不仅仅是Java。     

要回复问题请先登录注册