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类,但没有成功。学习如何使用示例代码会很有用。

编辑:

我创建了一个测试类ativeemethods:

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);
}

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

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* 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 %sn", 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 < (compptr_one->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 < (compptr_one->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.hproject/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 <stdio.h>
#include <setjmp.h>
#include <libjpeg-turbo/jpeglib.h>
#include <libjpeg-turbo/turbojpeg.h>
#include <libjpeg-turbo/jconfig.h>
#include <libjpeg-turbo/jmorecfg.h>
#include <libjpeg-turbo/jerror.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) {
        // 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库?

由于libjpeg_turbo被编译为一个C库,而你的Android本地代码被编译为一个c++程序,你应该通过链接指令包括C库。

试试这个,

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

如果需要全部从本机代码执行,可以在本机端为库创建自己的包装器。步骤大致如下:

  • 在你调用的类中:

    System.loadLibrary("libjpeg");

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

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

     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。运行ndk-build生成共享库。所以文件)。可能是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上快速搜索一下也是个好主意,看看是否有人在你制作相同的库之前付出了痛苦,或者只是看看一些比官方示例更高级的示例。

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

我把这个作为一个现有答案下的评论,但我认为这实际上是一个更好的答案,然后"你必须编写自己的包装器JNI库":

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

相关内容

  • 没有找到相关文章

最新更新