【问题标题】:C code needs to call Java code buffer but no way to get its JNIEnvC 代码需要调用 Java 代码缓冲区但无法获取其 JNIEnv
【发布时间】:2015-12-18 14:32:24
【问题描述】:

我有一个 java 缓冲区,我需要对它进行一些 c 数据操作。 为此,我调用了一个原生函数“SimpleFunction”,该函数调用 java 代码来检索特定索引处的缓冲区值 (buffer_read_byte) 并保存修改后的值 (buffer_byte_write)。

Java 端

byte buffer = new byte[100];
public static byte getByte(int index) {
    return buffer[index];
}   
public static void writeByte(int in, int index) {
    buffer[index] = (byte)(in);
}
private native void SimpleFunction(int size);

for(int i=0;i<100;i++)
    setBufferModByteInt(0x55,i);
SimpleFunction(100);

C端

#include <android/log.h>
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include "algorithms/global.h"
#define LOG_TAG "NDK"
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
static JavaVM* jvm;

void Java_com_App_SimpleFunction(JNIEnv * env, jobject this, jint size)
{
    int address=0;
    for(address=0;address<(int)(size);address++)
    {
        /*
        some data manipulation on ""value""
        */

        value=buffer_read_byte(env,this,address)&0xFF;
        buffer_byte_write(env,this,address,value);
    }
    return;
}

uint8_t buffer_read_byte(JNIEnv * env, jobject this, jint pos)
{
    uint8_t value=0;
    const char * ss="2";
    if ((*env)->ExceptionCheck(env)) {
     return;
    }

    jclass cls = (*env)->FindClass(env,"com/App/Class");
    if ((*env)->ExceptionCheck(env)) {
        return 0;
    }
    jmethodID method = (*env)->GetStaticMethodID(env, cls, "getByte", "(I)B");
    if ((*env)->ExceptionCheck(env)) {
     return 0;
    }
    jbyte result = (*env)->CallStaticByteMethod(env, cls, method,pos);
    if ((*env)->ExceptionCheck(env)) {
     return 0;
    }
    value=(uint8_t) result&0xFF;
    if(cls!=NULL)
    {
        (*env)->DeleteLocalRef(env,cls);
        if ((*env)->ExceptionCheck(env)) {
         return 0;
        }
    }
    return value;
}
void buffer_byte_write(JNIEnv * env, jobject this, jint pos, jint data)
{
    int value=data&0xFF;
    jclass cls = (*env)->FindClass(env,"com/App/Class");
    if ((*env)->ExceptionCheck(env)) {
        return;
    }
    jmethodID method = (*env)->GetStaticMethodID(env, cls, "writeByte", "(II)V");
    if ((*env)->ExceptionCheck(env)) {
     return;
    }
    (*env)->CallStaticVoidMethod(env, cls, method,value,pos);
    if ((*env)->ExceptionCheck(env)) {
     return;
    }
    if(cls!=NULL)
    {
        (*env)->DeleteLocalRef(env,cls);
        if ((*env)->ExceptionCheck(env)) {
         return;
        }
    }
    return;
}   

上面的代码有效。

现在让我们假设数据操作是由许多内部函数获得的,这些内部函数在内核中使用 c 函数 buffer_read_byte 和 buffer_byte_write。 如何避免在每个调用函数级别显式传递作为输入 (JNIEnv * env, jobject this)

我有很多预先存在的函数,最终需要使用 buffer_read_byte 和 buffer_byte_write 访问 java 缓冲区。 由于仅在内核函数 buffer_read_byte 和 buffer_byte_write 中需要 JNIEnv,如何避免在每个预先存在的父函数和子函数中必须声明输入 (JNIEnv * env, jobject this)

我检查了全局 JVM:

void Java_com_App_SimpleFunction(JNIEnv * env, jobject this, jint size)
{
    (*env)->GetJavaVM(env, &jvm);
    int address=0;
    for(address=0;address<(int)(size);address++)
    {
        value=buffer_read_byte(address)&0xFF;
        /*
        some data manipulation on ""value""
        */
        buffer_byte_write(address,value);
    }
    return;
}
uint8_t buffer_read_byte(jint pos)
{
    JNIEnv * env;
    jint rs=(*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4);
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6; // choose your JNI version
    args.name = NULL; // you might want to give the java thread a name
    args.group = NULL; // you might want to assign the java thread to a ThreadGroup
    jint rs=(*jvm)->AttachCurrentThread(jvm, (void**)&env, &args);

    //...

    return value;
}
uint8_t buffer_byte_write(jint pos, jint data)
{
    JNIEnv * env;
    jint rs=(*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4);
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6; // choose your JNI version
    args.name = NULL; // you might want to give the java thread a name
    args.group = NULL; // you might want to assign the java thread to a ThreadGroup
    jint rs=(*jvm)->AttachCurrentThread(jvm, (void**)&env, &args);

    //...

    return;
}   

但是调用 SimpleFunction(100) 没有执行(我没有对缓冲区进行数据修改)并且在尝试执行 GetEnvAttachCurrentThread 时导致应用崩溃>。

对于无法获取其 JNIenv 的 C 代码调用 JAVA 代码的最佳方式是什么?

【问题讨论】:

  • 没有办法获取JNIenv的C代码调用JAVA代码最好的方法是什么?给它一个获取env的方法。如果由于某种奇怪的原因你不能这样做,你需要编写一个可以访问它的代理函数,复制真实函数需要的数据,调用真实函数,然后根据需要复制回数据。

标签: java android c jvm-crash jnienv


【解决方案1】:

也许您需要找出它崩溃的原因。看起来你正在使用正确的方法。您也可以查看以下问题的答案。

How to create static JNI Environment Pointer?

How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    • 2021-08-17
    • 2011-08-11
    • 1970-01-01
    • 2013-08-12
    • 1970-01-01
    相关资源
    最近更新 更多