4

概括

  1. 使用 ByteBuffer.allocateDirect(someBufferSize) 在 Java 中创建称为缓冲区的 ByteBuffer
  2. 用数据填充缓冲区
  3. 将缓冲区作为作业对象传递给 C++ - jbuffer
  4. 使用 env->GetDirectBufferAddress(jbuffer) 获取缓冲区直接指针
  5. 在 C++ 端处理缓冲区数据。如何防止 GC 清理我们的缓冲区,否则它永远不会发生?
  6. 工作完成——我们现在不需要 jbuffer。
  7. 释放jbuffer?free(jbuffer) - 将引发无效地址错误

长部分

我使用下一个代码通过 Java AssetManager 加载 PNG 文件,以使用它们创建 Open GL ES 2.0 纹理。

Java端PNG类

import java.nio.ByteBuffer;
import android.graphics.Bitmap;

public class PNG 
{
 private static final int BYTES_PER_PIXEL_PNG = 4;
 private static final String LOG_TAG = "[PNG]";
 public int width;
 public int height;
 public ByteBuffer pixels;


 public PNG(Bitmap bitmap)
 {
  this.width = bitmap.getWidth();
  this.height = bitmap.getHeight();
  this.pixels = ByteBuffer.allocateDirect(this.width * this.height * BYTES_PER_PIXEL_PNG);
  bitmap.copyPixelsToBuffer(this.pixels);
 }
}

public static PNG loadPNG(String path)
{
    InputStream is = null;
    try
    {
        is = ASSETS_MANAGER.open(path);//get png file stream with AssetsManager instance
    }
    catch (IOException e)
    {
        Log.e(LOG_TAG, "Can't load png - " + path, e);
    }

    return new PNG(BitmapFactory.decodeStream(is));
}

C++ 端 PNG

typedef struct png
{
int width;
int height;
char* pixels;
} png;

png* load_png(const char* path)
{
 png* res = (res*) malloc(sizeof(png);
 ...
 jobject _png = env->CallStaticObjectMethod(get_java_lib_class(), get_method_id(JAVA_LIB_LOAD_PNG, JAVA_LIB_LOAD_PNG_SIGN), _path);//Calling loadPng() from Java, get PNG jobject
 jobject _pixels =  env->GetObjectField(_png, PNG_FIELDS->PNG_PIXELS_ID);//Getting pixels field from Java PNG jobject
 res->pixels = (char*) env->GetDirectBufferAddress(_pixels);//Get direct pointer to our pixel data
 //Cleanup
 ...
 env->DeleteLocalRef(_png);
 env->DeleteLocalRef(_pixels);
 return res;
}

然后使用 png 创建纹理

void test_create_tex(const char* path)
{
 ...
 png* source = load_png(path);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source->width, source->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, source->pixels);
//We don't need source pixel data any more
//free(source->pixels);//INVALID HEAP ADDRESS (deadbaad)
free(source);
}

那么在 C++ 端使用它的直接指针后如何释放字节缓冲区呢?它是直接分配的(如 malloc - 在本机端)并且必须被释放,否则我会得到 OutOfMemory 错误。

4

2 回答 2

4

您不需要释放缓冲区。您已经在 J​​ava 端分配了它,这意味着它是 JVM 对象,GC 会处理它。与在 C 端分配相反,因此是 GC 不知道的本机对象。您甚至不需要这样做DeleteLocalRef,因为在从本地方法返回时,JNI 机器将为您删除所有本地引用。只有在一个本机调用范围内有数百个 JNI 调用返回 JVM 时,您才需要显式删除,因此即使在返回 JVM 之前,您也会用完句柄。

我必须承认,我不确切知道 GC 是如何知道它不应该触及您的 ByteBuffer,但我猜想通过调用GetObjectField您正在增加 ByteBuffer 上的 refcount 并减少DeleteLocalRef. 所以在这两个 JNI 调用之间,ByteBuffer 是安全的。

于 2013-02-11T14:21:58.413 回答
2

在我看来,你不必担心 的释放ByteBuffer pixels,因为它是由 JVM 管理的。您真正应该关心的是在 C++ 使用它时防止它被垃圾收集。

于 2013-02-07T12:58:57.310 回答