java - Android内存管理从 native JNI调用Java类并声明数据(用于图像转换)

标签 java android java-native-interface jnienv

我正在为 Android 编写 native 代码,我希望在其中解压缩数据 block 。我正在从 native JNI 函数调用 Java 方法。此 Java 方法调用 BitmapFactory,然后尝试使用以下命令分配一些内存:

int[] pixels = new int[width * height];

当程序似乎崩溃或停止或发生其他情况时,给我“挂起时旋转”,然后在 VM 关闭之前 logcat 中出现大量涌出。

为了在深入了解细节之前先给出大局,我试图将压缩图像的字节数组转换为 width*height*bpp 未压缩图像以返回给 native 代码。我正在尝试使用 Android Java BitmapFactory 来进行解压缩,这似乎工作正常。压缩的字节 block 是之前加载到 native 代码中的 JPG 或 PNG 图像(并且无法从我的系统中的磁盘读取)。

顺序有点复杂,我不确定什么是相关的,所以我会全部说出来: 类 MyRenderer 实现 GLSurfaceView.Renderer 方法 onSurfaceCreated 调用 native init(),该方法在 MyRenderer 类中定义:

static {System.loadLibrary("glprog");
}
public native void init(int dummy_variable);

在 native 代码中,这是调用的函数:

JNIEXPORT void JNICALL Java_com_pitransviewersingleimageres_MyRenderer_init(JNIEnv * env, jobject obj,  jint dummy_variable) {
glprog_init(dummy_variable);
}

然后调用另一个 native 函数char glprog_init(int dummy_variable)它本身调用 native wrapper_uncompress_image_by_os(overlay_compressed_data,overlay_compressed_data_len,... 这是 native 函数:

char wrapper_uncompress_image_by_os(unsigned char *compressed_data, int compressed_len,                     //input compressed data
                                char *data_name_string, char *suffix,                                   //name and type
                                int slot_num)  {
jstring jnamestring = (*preset_env)->NewStringUTF(preset_env,data_name_string);
jstring jsuffix = (*preset_env)->NewStringUTF(preset_env,suffix);
//create byte[] and copy data in
jbyteArray retArray = (*preset_env)->NewByteArray(preset_env, compressed_len);
if(retArray==NULL) {__android_log_write(ANDROID_LOG_INFO, "NATIVE","ERROR wrapper_uncompress_image_by_os: calling NewByteArray()");return-1;}
jbyte *javaptr = (*preset_env)->GetPrimitiveArrayCritical(preset_env, (jarray)retArray, 0);
if(javaptr==NULL) {__android_log_write(ANDROID_LOG_INFO, "NATIVE","ERROR wrapper_uncompress_image_by_os: calling GetPrimitiveArrayCritical()");return-1;}
memcpy(javaptr,compressed_data,compressed_len); 
(*preset_env)->ReleasePrimitiveArrayCritical(preset_env, retArray, javaptr, 0);

if((preset_javaobject!=NULL)&&(preset_method_id_convertimagefromnative!=NULL))
   {
   jthrowable exception;
   //call java function
   (*preset_env)->CallVoidMethod(preset_env,     preset_javaobject,preset_method_id_convertimagefromnative, 
                             retArray,compressed_len,
                             jnamestring,jsuffix,slot_num);

它在调用 native init() 的同一个 Java 类 MyRenderer 中调用方法 convertImageFromNative()

   public void convertImageFromNative(final byte[] data, int data_len, final String data_name_string, final String suffix, final int slot_num) {
    //writeFile(data,data_name_string);
    Bitmap bitmapqq=bytes2bitmap(data);

}

到目前为止,这似乎有效,如果我取消注释 writeFile() ,它会将所有数据写入我的 SD 卡。我在 bytes2bitmap() 内部遇到了问题。

可以从“常规”Java 中调用 bytes2bitmap() 类,但是当从 native 调用的这个类调用时,它会在 int[] Pixels = new int 处崩溃[宽度*高度];行。

最后这是导致崩溃的代码:

Bitmap bytes2bitmap(byte[] bytes) {
    Log.d("startup", "Entered MyRenderer.bytes2bitmap()");
    BitmapFactory.Options opt = new BitmapFactory.Options(); 
    opt.inDither = true; 
    opt.inPreferredConfig = Bitmap.Config.ARGB_8888; 
    Bitmap bitmap=BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt);
    if(bitmap==null) Log.d("running", "ERROR: GLESActivity.java: BitmapFactory() returns null");
    int width = bitmap.getWidth(); 
    int height = bitmap.getHeight(); 
    bytes=null;   //not sure if I should be freeing memory here, but it seems to work
    int[] pixels = new int[width * height];    //crash happens here
    //crashes before doing the following
    bitmap.getPixels(pixels, 0, width, 0, 0, width, height); 
    pixels=null;
    return bitmap;
}

我还没有达到将未压缩数据正确传递回 native 代码的部分,但我认为我应该首先解决这个问题。

我可以在 native 代码中创建一个 int[] 数组来传递,问题是我不知道未压缩的图像大小。

提前致谢, 标记

最佳答案

我解决了我的问题,这是我未显示的代码中的错误。

问题出在 native 代码中的(*preset_env)->CallVoidMethod(preset_env,preset_javaobject,preset_method_id_convertimagefromnative,调用中。变量preset_env,_javaobject,_method_id_设置不正确。我将它们设置为启动一次后可以使用 native 函数 _setupnative2java() 重新使用。我的项目中有三个类。我的错误是从我的第一个 Activity 类调用 _setupnative2java() ,而不是实际包含 java 方法的 MyRenderer 类bytes2bitmap()。在调试过程中,我将此函数的另一个副本放在同名的第一个 Activity 类中。所以它有点工作,但指针错误。奇怪的是,对 MyRenderer 的修改和 logcat 调用会工作。

我实际上确实是从 MyRenderer 类调用它的,但是是从带有 instancename.MyRenderer.setupnative2java 的启动 Activity 类调用的。但也许因为它是从另一个类调用的,所以它没有获取 MyRenderer 的指针。另外,我不确定 MyRenderer 到底是如何“初始化”的。如果您为此花费了脑力,我深表歉意。

void Java_com_pitransviewersingleimageres_MyRenderer_setupnative2java(JNIEnv* env, jobject,javaThis, jint unused_variable) 
    {
    jthrowable exception;
    jclass class_id;
    preset_env=env;
    preset_javaobject=javaThis;
    class_id = (*env)->GetObjectClass(env, javaThis);
    if(class_id!=NULL)
       {
       preset_method_id_convertimagefromnative = (*env)->GetMethodID(env, class_id, "convertImageFromNative", "([BILjava/lang/String;Ljava/lang/String;I)V");  
       }
...

关于java - Android内存管理从 native JNI调用Java类并声明数据(用于图像转换),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12428653/

相关文章:

java - Gluon Connect 在使用 Charm 依赖项进行 POST 时返回 NPE > 4.3.3

java - 将变量和对象设置为 null 有帮助吗?

java - Java 中的 "unmappable character for encoding"警告

Android,日文字 rune 件名比较问题

android - 如何使相机预览透明?

android - 从其他 jni 库调用 jni 方法

Java Applet + JNI + .so 文件

java - 如何从返回多条记录的java调用RPGIV程序

android - Composer 的损坏测试报告

java - 什么是缓存字符串?