【问题标题】:Returing data from JNI fuction to Android application code将数据从 JNI 函数返回到 Android 应用程序代码
【发布时间】:2018-01-10 20:34:57
【问题描述】:

我正在开发一个 Android 应用程序,为此我使用了一个本机 C 库。该库用于文件解压。

native方法的定义如下:

public static native Long decompress(ByteBuffer src, ByteBuffer dst);

我使用这个功能的应用代码如下:

try {
            File inFile = new File(Environment.getExternalStorageDirectory().getPath() +"/1"); // source file

            ByteBuffer buf = ByteBuffer.allocateDirect((int)inFile.length());

            ByteBuffer buf_out = ByteBuffer.allocateDirect((int)inFile.length()*20);

            InputStream is = new FileInputStream(inFile);

            int b;

            while ((b=is.read())!=-1) {
                buf.put((byte)b);
            }
            Log.d("nims","source buffer zie"+buf.position());
            File file = new File(Environment.getExternalStorageDirectory().getPath() +"/2");

            // append or overwrite the file
            boolean append = false;

            FileChannel channel = new FileOutputStream(file, append).getChannel();

            Long t = decompress(buf,buf_out); // t is some XYZ number

            Log.d("nims","dest buffer zie"+buf_out.position()); // buf_out.position() returns 0
            buf_out.flip();

            // Writes a sequence of bytes to this channel from the given buffer.
            channel.write(buf_out);

            // close the channel
            channel.close();

        }
        catch (IOException e) {
            System.out.println("I/O Error: " + e.getMessage());
        }

JNI 代码

JNIEXPORT jlong JNICALL 

    Java_com_company_appName_MainActivity_decompress(JNIEnv* env, jclass cls, jobject src, jobject dst) {

        uint8_t* src_buffer = (*env)->GetDirectBufferAddress(env,src);
        const size_t src_size = (*env)->GetDirectBufferCapacity(env, src);

        uint8_t* dst_buffer = (*env)->GetDirectBufferAddress(env,dst);

        size_t dst_size = (*env)->GetDirectBufferCapacity(env, dst);
        jlong test = _decode_buffer(dst_buffer, dst_size, src_buffer, src_size, NULL);

        return test;
    }

目标缓冲区不包含任何数据。

我有以下问题:

  1. 如何将目标字节缓冲区从 JNI 代码读回应用程序代码?
  2. 如何将目标字节缓冲区数据写入文件?

我将不胜感激有关此主题的任何建议和想法。

编辑 1:

size_t test = lzfse_decode_buffer(dst_buffer, dst_size, src_buffer, src_size, NULL);

    ALOG("Size of test %d.",test); // output is 319488
    ALOG("Size of test after casting  %ld.",(jlong)test); // output is -125648933


    //New code from stack overflow
    jclass cls2 = (*env)->GetObjectClass(env, dst);
    jmethodID limitId = (*env)->GetMethodID(env, cls2, "limit", "(I)Ljava/nio/Buffer;");
    (*env)->CallObjectMethod(env, dst, limitId,(jlong) test);
    jmethodID positionId = (*env)->GetMethodID(env, cls2, "position", "(I)Ljava/nio/Buffer;");
    (*env)->CallObjectMethod(env, dst, positionId, 0);

编辑 2:

size_t test = lzfse_decode_buffer(dst_buffer, dst_size, src_buffer, src_size, NULL);
     ALOG("Size of test %d.",test); // output is 319488
    ALOG("Size of test after casting  %d.",(jint)test); // output is 319488
    //New code from stack overflow
    jclass cls2 = (*env)->GetObjectClass(env, dst);
    jmethodID limitId = (*env)->GetMethodID(env, cls2, "limit", "(I)Ljava/nio/Buffer;");
    (*env)->CallObjectMethod(env, dst, limitId,(jint) test);
    jmethodID positionId = (*env)->GetMethodID(env, cls2, "position", "(I)Ljava/nio/Buffer;");
    (*env)->CallObjectMethod(env, dst, positionId, 0);

【问题讨论】:

  • 首先,您的本机方法返回 long(原始类型),而不是 Long(扩展 Object) .我不确定此修复程序是否会解决您的问题。此外,最好的做法是检查 JNI 代码中的返回值和错误,确保您的 GetDirectBufferAddress() 成功。
  • 谢谢您的回复。我确实检查了我的 JNI 代码,它工作正常。我面临的唯一问题是我如何才能定位 ByteBuffer 值?
  • 确保你的代码有#include "lzfse.h"

标签: java android c android-ndk


【解决方案1】:

当您在 C 中写入 DirectByteBuffer 时,这不会改变大小 (limit) 和 position dst 缓冲区。您必须自己设置这些。您可以在本机代码中执行此操作:

jclass cls = (*env)->GetObjectClass(env, dst);
jmethodID limitId = (*env)->GetMethodID(env, cls, "limit", "(I)Ljava/nio/Buffer;");
(*env)->CallObjectMethod(env, dst, limitId, actual_size);
jmethodID positionId = (*env)->GetMethodID(env, cls, "position", "(I)Ljava/nio/Buffer;");
(*env)->CallObjectMethod(env, dst, positionId, 0);

这假设您可以从 _decode_buffer() 函数中检索写入 dst 缓冲区的实际字节数。

而且你不需要 flip()

【讨论】:

  • 感谢您的回复。我会尝试您建议的解决方案,并让您知道该解决方案是否有效。
  • 我的“_decode_buffer()”函数返回 size_t 中的值,但我需要 jlong​​ 中的 actual_size 值。如何在这些数据类型之间进行转换?
  • 出于所有实际目的,您可以安全地将 size_t 转换为 jlong​​。如果您的输出数组有可能超过 9,223,372,036,854,775,807,您应该重新考虑您的架构决策。
  • 我尝试了这种方法,但代码正在生成异常“错误限制(容量 687040):-1264855663”
  • 终于修复了!!!我使用 Java 代码来设置限制和位置参数。非常感谢。你应该得到奖励!!!
猜你喜欢
  • 1970-01-01
  • 2019-10-11
  • 2018-01-19
  • 1970-01-01
  • 2017-11-30
  • 2019-06-09
  • 2021-06-21
  • 1970-01-01
  • 2010-12-09
相关资源
最近更新 更多