我有用C ++编写的 .so (共享库),我可以将其称为 functional.so ,其中我实现了不同的功能,这里列出了一些函数: / p>
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 );
依旧......
我想在我的java应用程序中使用这个库的( functional.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
声明初始化,取消初始化, 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);
`
感谢您的帮助。
答案 0 :(得分:8)
您可以使用jlong
将指针(或指针指针等)传递回Java。 Java代码将无法将其用于任何事情,除了将其作为参数传递给您的其他方法之外;但通常这就是你真正想要的。另一方面,如果您希望使用Java中的数据设置调用Initialize()
,则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);
}
然后在Java中使用它们(没有效果!):
long ptr = utils.malloc(100);
// Store ptr for a while
utils.free(ptr);
现在,如果我们将一些需要内存块作为参数的其他函数包装起来,我们也可以将它们包装起来,让它们接受jlong
参数,就像free()
一样。 Java变量ptr
表示内存地址的事实在Java中是完全不透明的,但它仍然有用。
Java的窗口系统实现(即,AWT,SWT)使用同样的东西将本机窗口小部件句柄与Java组件相关联。
现在,如果您希望Initialize()
能够从Java中获取有用的参数,那么void *
不会削减它。您需要编写自己的方法来接受Java对象作为参数;这是允许您在Java中操作对象的唯一方法。
我不想在这里复制所有代码,但是Sun的JNI教程是here。 This是关于调用Java对象的任意方法的部分(this
对象,或者作为参数传递给方法的对象)和this是访问字段的类似部分一个对象。
答案 1 :(得分:6)
你想要做的是: 对于void *函数,使用直接字节缓冲区。在Java方面:
native long initialize(java.nio.ByteBuffer userData);
调用方法时,请务必分配直接的ByteBuffer(请参阅java.nio.ByteBuffer和JNI nio usage):
ByteBuffer myData = ByteBuffer.allocateDirect(size);
long res = initialize(myData);
在C方面,你这样做:
unsigned long res = Initialize(env->GetDirectBufferAddress(env, buffer));
return (jlong)res;
您使用ByteBuffer方法从Java端的缓冲区读取和写入。
您还可以使用env->NewDirectByteBuffer(env, ptr, size
在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 array operations
答案 2 :(得分:1)
到目前为止,我认为@pron会回答最好的一个。
关于自动生成JNI代码,请考虑jnigen 它使用起来非常简单,功能强大。
获取Javadoc,并查找 NativeCodeGenerator 类。 它具有Java类型和本机CPP类型之间的应用映射,如String *到char *,int []到int *,FloatBuffer到float *等。
Jnigen,如GitHub所述
jnigen是一个小型库,可以与libgdx一起使用,也可以不与libgdx一起使用,它允许C / C ++代码与Java源代码一起编写。
jnigen有两部分:
检查特定文件夹中的Java源文件,检测本机方法和附加的C ++实现,并吐出C ++源文件和标题,类似于您使用JNI手动创建的文件和标题。
< / LI>为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生成器后,您将获得带有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);
*/
答案 3 :(得分:0)
您是否考虑过使用自动包装生成器?我现在几次使用SWIG取得了很大的成功。我提到它的原因是它有很好的模式来处理诸如指针之类的东西,包括void*
等等。
如果您要包装的方法与上面列出的方法一样简单,则包装器生成就像传入库的头文件一样简单。即使您有一些更复杂的方法(传递结构,返回指向内存的指针等),SWIG允许您在需要时向生成的包装器添加代码。
另一个优点是,SWIG可以包含多种语言,而不仅仅是Java。