Android如何通过JNi示例使用libjpeg-turbo库

我用ndk-build成功构建了libjpeg-turbo,感谢这篇文章: libjpeg-turbo for android

我想在libjpeg-turbo的example.c中获得像read_JPEG_file这样的本机函数来从Java代码调用它以用于Android应用程序。

有人能给我一个例子怎么做吗? 如何编写使用libjpeg-turbo ndk构建库的Java本机方法?

我可以通过加载库

System.loadLibrary("libjpeg"); 

但接下来呢? 该库没有任何本机方法可以从Java调用。

我试图根据JNI文档编写一个JNI c类但没有成功。 示例代码非常适合学习如何操作。

编辑:

我创建了一个测试类NativeMethods:

 package com.test.app; public class NativeMethods { private String filename = null; static { System.loadLibrary("jpeg"); // this represents compiled libjpeg-turbo under ndk } public NativeMethods(String jpegFilename) { this.filename = jpegFilename; } public native int computeNumberOfDCTS(String filename); } 

然后我使用javah生成本机方法的C头,结果是com_test_app_NativeMethods.h文件包含:

 /* DO NOT EDIT THIS FILE - it is machine generated */ #include  /* Header for class com_test_app_NativeMethods */ #ifndef _Included_com_test_app_NativeMethods #define _Included_com_test_app_NativeMethods #ifdef __cplusplus extern "C" { #endif /* * Class: com_test_app_NativeMethods * Method: computeNumberOfDCTS * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif 

然后我创建了名为JPEGProcessing.c文件,其中我将本机函数的c实现如下:

 #include "com_test_app_NativeMethods.h" //that came from jpeglib example struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr * my_error_ptr; //routine that will replace the standard error_exit method static void my_error_exit (j_common_ptr cinfo) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message) (cinfo); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); } JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS (JNIEnv *env, jobject obj, jstring javaString) { jint toReturn = 0; // struct representing jpeg image struct jpeg_decompress_struct cinfo; // struct representing error manager; defined above struct my_error_mgr jerr; const char *filename = (*env)->GetStringUTFChars(env, javaString, 0); FILE * infile; if ((infile = fopen(filename, "rb")) == NULL) { fprintf(stderr, "can't open %s\n", filename); return -1; } cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(infile); return -1; } jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); (void) jpeg_read_header(&cinfo, TRUE); // declare virtual arrays for DCT coefficients jvirt_barray_ptr* coeffs_array; // read DCT coefficients from jpeg coeffs_array = jpeg_read_coefficients(&cinfo); // fill virtual arrays. // ci represents component color // this cycle prints all dct coefficient of the jpeg in 8x8 blocks for (int ci = 0; ci < 3; ci++) { JBLOCKARRAY buffer_one; JCOEFPTR blockptr_one; jpeg_component_info* compptr_one; compptr_one = cinfo.comp_info + ci; for (int by = 1; by height_in_blocks - 1); by++) // we don't want to use the edges of the images { buffer_one = (cinfo.mem->access_virt_barray)((j_common_ptr)&cinfo, coeffs_array[ci], by, (JDIMENSION)1, FALSE); for (int bx = 1; bx width_in_blocks - 1); bx++) // we don't want to use the edges of the images { blockptr_one = buffer_one[0][bx]; for (int bi = 1; bi < 64; bi++) // we don't want to use AC { toReturn++; } } } } jpeg_destroy_decompress(&cinfo); fclose(infile); return toReturn; } 

文件JPEGProcessing.ccom_test_app_NativeMethods.h位于project/jni/文件夹中。 在同一个jni文件夹中,我创建了Android.mk文件,其中我放了这些行:

 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := JPEGProcessing LOCAL_CFLAGS := -Werror LOCAL_SRC_FILES := JPEGProcessing.c LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_LDLIBS := -lm -llog -landroid LOCAL_STATIC_LIBRARIES := libjpeg include $(BUILD_SHARED_LIBRARY) 

从我的项目目录运行ndk-build后出现了一些错误:

 jni/JPEGProcessing.c:7:27: error: field 'pub' has incomplete type jni/JPEGProcessing.c:8:5: error: unknown type name 'jmp_buf' jni/JPEGProcessing.c:14:16: error: unknown type name 'j_common_ptr' jni/JPEGProcessing.c: In function 'Java_com_test_app_NativeMethods_computeNumberOfDCTS': jni/JPEGProcessing.c:30:36: error: storage size of 'cinfo' isn't known jni/JPEGProcessing.c:36:5: error: unknown type name 'FILE' jni/JPEGProcessing.c:38:17: error: assignment makes pointer from integer without a cast [-Werror] jni/JPEGProcessing.c:38:45: error: 'NULL' undeclared (first use in this function) ... 

我不明白。 如何将代码与libjpeg-turbo函数结合起来获取工作库并在java中使用它?

我阅读了NDK指令和示例,但仍然没有得到它。

编辑:

使用简单本机方法的NDK和JNI工作得很好。 作为测试,我使用了以下简单的方法:

 #include "com_test_app_NativeMethods.h" JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS (JNIEnv *env, jobject obj, jstring javaString) { jint toReturn = 0; const char *filename = (*env)->GetStringUTFChars(env, javaString, 0); const char *test = "test"; if ( filename == test ) { toReturn++; } return toReturn; } 

我用libjpeg-turbo的东西尝试了如下:

 #include "com_test_app_NativeMethods.h" #include  #include  #include  #include  #include  #include  #include  //that came from jpeglib example struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr * my_error_ptr; //routine that will replace the standard error_exit method static void my_error_exit (j_common_ptr cinfo) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message) (cinfo); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); } JNIEXPORT jint JNICALL Java_com_test_app_NativeMethods_computeNumberOfDCTS (JNIEnv *env, jobject obj, jstring javaString) { jint toReturn = 0; // struct representing jpeg image struct jpeg_decompress_struct cinfo; // struct representing error manager; defined above struct my_error_mgr jerr; const char *filename = (*env)->GetStringUTFChars(env, javaString, 0); FILE * infile; if ((infile = fopen(filename, "rb")) == NULL) { // cannot open file return -1; } cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(infile); return -1; } jpeg_create_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return toReturn; } 

我得到错误……:

 Compile thumb : JPEGProcessing <= JPEGProcessing.c SharedLibrary : libJPEGProcessing.so /usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:50: error: undefined reference to 'jpeg_std_error' /usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:59: error: undefined reference to 'jpeg_CreateDecompress' /usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:61: error: undefined reference to 'jpeg_destroy_decompress' /usr/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/JPEGProcessing/JPEGProcessing.o: in function Java_com_test_app_NativeMethods_computeNumberOfDCTS:jni/JPEGProcessing.c:54: error: undefined reference to 'jpeg_destroy_decompress' collect2: ld returned 1 exit status make: *** [obj/local/armeabi/libJPEGProcessing.so] Error 1 

那么如何在ndk构建的libjpeg-turbo库中使用本机代码呢?

如果需要从本机代码执行所有操作,可以在本机端为库创建自己的包装器。 步骤大致是这些步骤:

  • 在你打电话的class级:

    的System.loadLibrary( “的libjpeg”);

您声明了需要在Java端调用的本机方法,而不提供任何实现:

 public static native readJPEGFile(); 
  • 然后,您可以使用javah命令创建一个C文件,并在本机端实现本机function。 当你在Java端调用本机函数时,将调用它。 注意函数和参数的完整路径,它们将由javah命令自动生成,不应更改,否则将无法正常工作。 结果应该是这样的:

      extern "C" { JNIEXPORT void JNICALL Java_com_android_packagename_GL2JNILib_readJPEGFile(JNIEnv * env, jobject obj); }; JNIEXPORT void JNICALL Java_com_android_packagename_GL2JNILib_readJPEGFile(JNIEnv * env, jobject obj) { // Call your library's function } 
  • 之后,您需要在包含包装函数的jni文件夹(如果它不存在的话创建它)中创建共享库。 您可以创建一个Android.mk文件并运行ndk-build来生成共享库(.so文件)。 一个可能的Android.mk可能就像这个,你应该检查名称和依赖关系:

      LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := jpgturbo-wrapper LOCAL_CFLAGS := -Werror LOCAL_SRC_FILES := jpgturboWrapper.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_LDLIBS := -lm -llog -landroid LOCAL_STATIC_LIBRARIES := jpegturbo # write all the libraries your project depend on here, separated by white spaces include $(BUILD_SHARED_LIBRARY) 
  • 现在您可以运行ndk-build,并生成.so文件并将其复制到其对应的文件夹中。

您可以查看NDK文档,特别是它们提供的样本: http : //developer.android.com/tools/sdk/ndk/index.html

有时也是一个好主意,快速搜索github,看看是否有其他人在你做同样的库工作之前就已经痛苦,或者只是为了看一些比官方样本更高级的例子。

由于libjpeg_turbo被编译为C库并且您的Android本机代码被编译为C ++程序,因此您应该通过linkage指令包含C库。

尝试这个,

 extern "C" { #include "headers of libjpeg_turbo" } 

尝试修改你的Android.mk以包含你的libjpeg头,也试着找一些关于dlopen()函数的信息来使用你编译的arm libjpeg.so。

我在现有的答案中发布了这个评论作为评论,但我认为这实际上是一个更好的答案,然后“你必须编写自己的包装器JNI lib”:

根据@AlexCohn的评论 ,您可以在构建中包含包装器JNI接口,方法是将turbojpeg-jni.c添加到要在Android.mk构建的文件列表中,这样您就可以使用它们直接提供的Java类来代替必须创建自己的本机代码来调用lib。