android jni return 多个变量

android jni return multiple variables

如何从 jni 函数 return 多个变量,避免数组的开销 mangaling?

您可以使用 包装器。然后 return 包装器。首先创建一个 class,其中包含一些内部变量,这些变量是您想要 return 的多个变量。然后使用这个 class 作为 return 类型。

我可以想出三种不同的方法。

回调

从您的 JNI 代码中调用一个带有多个参数的 Java 方法,在您的 Java 代码中的某处设置一个变量,您可以在从该方法 returning 时检索该变量。

JNIEXPORT void JNICALL Java_my_package_name_JNIReturnExample_returnWithJavaCallback(JNIEnv *env, jobject javaThis, jfloat param1, jfloat param2)
{
    // Get the class of the current calling object
    jclass clazz = (*env)->GetObjectClass(env, javaThis);

    // Get the method id of the instance method: void javaCallback(float, float) in my.package.name.JNIReturnExample
    jmethodID callback = (*env)->GetMethodID(env, clazz, "javaCallback", "(FF)V");

    // Calls my.package.name.JNIReturnExample#javaCallback(float, float);
    (*env)->CallVoidMethod(env, javaThis, callback, param1, param2);
}

Return一个新的Java对象

在 JNI 中实例化一个 Java 对象 (my.package.name.JNIReturnExample),并将其 return 到 Java。

JNIEXPORT jobject JNICALL Java_my_package_name_JNIReturnExample_returnObjectValue(JNIEnv *env, jobject javaThis, jfloat param1, jfloat param2)
{
    // Get the class we wish to return an instance of
    jclass clazz = (*env)->FindClass(env, "my/package/name/JNIReturnObject");

    // Get the method id of an empty constructor in clazz
    jmethodID constructor = (*env)->GetMethodID(env, clazz, "<init>", "()V");

    // Create an instance of clazz
    jobject obj = (*env)->NewObject(env, clazz, constructor);

    // Get Field references
    jfieldID param1Field = (*env)->GetFieldID(env, clazz, "param1", "F");
    jfieldID param2Field = (*env)->GetFieldID(env, clazz, "param2", "F");

    // Set fields for object
    (*env)->SetFloatField(env, obj, param1Field, param1);
    (*env)->SetFloatField(env, obj, param2Field, param2);

    // return object
    return obj;
}

传递一个Java对象作为参数,并设置它的字段

在您的 Java 代码中创建一个 Java 对象的新实例,并将该对象作为参数传递给您的 JNI 函数。

JNIEXPORT void JNICALL Java_my_package_name_JNIReturnExample_setObjectFields(JNIEnv *env, jobject javaThis, jobject obj, jfloat param1, jfloat param2)
{
    // Get the class of the input object
    jclass clazz = (*env)->GetObjectClass(env, obj);

    // Get Field references
    jfieldID param1Field = (*env)->GetFieldID(env, clazz, "param1", "F");
    jfieldID param2Field = (*env)->GetFieldID(env, clazz, "param2", "F");

    // Set fields for object
    (*env)->SetFloatField(env, obj, param1Field, param1);
    (*env)->SetFloatField(env, obj, param2Field, param2);
}

请注意,无论您决定使用哪种方法,都应该缓存各种 JNI 类型 jclass, jmethodID, jfieldID,因为 JNI 查找操作很慢,而且实际上只需要执行一次。


缓存

在回调方法中缓存 JNI 引用,并使用方法调用它们:

static jclass myCallbackClass;
static jmethodID myCallbackMethod;

/**
 * Call this method in JNI_OnLoad
 */ 
void CacheCallback()
{
    // Get a reference to the Callback class
    jclass clazz = (*env)->FindClass(env, "my/package/name/JNIReturnExample");

    // Store a global reference, since the local one will be freed when returning from the function.
    myCallbackClass = (*env)->NewGlobalRef(env, clazz);

    // Get a reference to the static callback method
    jmethodID callback = (*env)->GetStaticMethodID(env, myCallbackClass, "jniCallback", "(II)V");

    // jmethodID doesn't need a NewGlobalRef call
    myCallbackMethod = callback;
}

/**
 * Call this method in JNI_OnUnload
 */
void ReleaseCallback()
{
    (*env)->DeleteGlobalRef(env, myCallbackClass);
    myCallbackClass = NULL;

    // jmethodIDs are safe to keep without an explicit global reference, for this reason, we don't need to delete the reference either.
    myCallbackMethod = NULL;
}

还有一种方式——jobjectArray

就我个人而言,我需要 return 到 Java 一对字符串和整数。

可以使用 env->NewStringUTF 方法轻松地将字符串放入此类数组中 但是对于 int 必须构造一个包装器。

原生部分:

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_example_myapp_CallingClass_writeCfgFile(
    JNIEnv *env,
    jobject obj,
    jobjectArray stringArray,
    jstring filepath){
...     
    std::pair<int,string> ret = generate_config(filePath, reqMsgTypes);

    jobjectArray retobjarr = (jobjectArray)env->NewObjectArray(2, env->FindClass("java/lang/Object"), NULL);
    env->SetObjectArrayElement(retobjarr, 0, NewInteger(env, ret.first));
    env->SetObjectArrayElement(retobjarr, 1, env->NewStringUTF(ret.second.c_str()));

    return retobjarr;
}

jobject NewInteger(JNIEnv* env, int value){
    jclass integerClass = env->FindClass("java/lang/Integer");
    jmethodID integerConstructor = env->GetMethodID(integerClass, "<init>", "(I)V");
    return env->NewObject(integerClass, integerConstructor, static_cast<jint>(value));
}

Java 一侧:

Object[] resp = writeCfgFile( msgTypes,  filePath");
Integer i = (Integer)resp[0];
String str = (String)resp[1];