Как вызвать функцию Java и передать аргументы из С++ с помощью Android NDK

Я пытаюсь вызвать функцию Java из своего кода на С++, но приложение продолжает «зависать».

Сначала я запускаю код C++ через вызов JNI, который работает без проблем. Затем я позволяю функции, которая вызывается, выполнять обратный вызов:

#include <jni.h>

extern "C"
JNIEXPORT jstring JNICALL
Java_net_example_folder_Service_startSomething(JNIEnv *env, jobject obj) {
    invoke_class(env);
    return env->NewStringUTF("The End.\n");    //works if I only use this line
}

Попытка подписаться на http://www.inonit.com/cygwin/jni/invocationApi/c.html (и множество других руководств/советов и т. д.), я использую это для вызова функции java:

void invoke_class(JNIEnv* env) {
    jclass helloWorldClass;
    jmethodID mainMethod;

    helloWorldClass = env->FindClass("Java/net/example/folder/helloWorldClass");

    mainMethod = env->GetStaticMethodID(helloWorldClass, "helloWorld", "()V");

    env->CallStaticVoidMethod(helloWorldClass, mainMethod);
}

Чтобы вызвать код Java:

package net.example.folder;

import android.util.Log;

public class helloWorldClass {
    public static void helloWorld() {
        Log.e("helloWorldCLass", "Hello World!");
    }
}

Код С++ вызывается фоновой службой. Вот функция Activity, которая его запускает:

public void startService() {
    Intent i = new Intent(this, Service.class);
    startService(i);
}

А это часть Сервиса:

public class SimService extends IntentService {

    ...

    @Override
    protected void onHandleIntent(Intent intent) {
        System.loadLibrary("native-lib");
        startSomething();
    }
}

Все это работает, но когда я теперь изменяю функцию «invoke_class» на:

void invoke_class(JNIEnv* env) {
    jclass helloWorldClass;
    jmethodID mainMethod;

    helloWorldClass = env->FindClass("net/example/folder/helloWorldClass");

    mainMethod = env->GetStaticMethodID(helloWorldClass, "helloWorld", "([Ljava/lang/String;)V");

    env->CallStaticVoidMethod(helloWorldClass, mainMethod, env->NewStringUTF("some text"));
}

и, конечно же, java-часть:

package net.example.folder;

import android.util.Log;

public class helloWorldClass {
    public static void helloWorld(String msg) {
        Log.e("helloWorldCLass", msg);
    }
}

С этим я получу ранее упомянутый сбой.

Это почему? Как правильно передать аргументы?


person janonymous    schedule 31.03.2018    source источник
comment
[ используется для представления массивов (как если бы у вас было String[] msg), удалите это из вашего метода GetStaticMethodID.   -  person Dmitrii Z.    schedule 01.04.2018
comment
И, конечно же, добавьте проверку ошибок для всех вызовов JNI. Не стоит только предполагать, что они добьются успеха.   -  person Michael    schedule 01.04.2018
comment
Спасибо, без [ работает. Ошибка проверки типа if(mainMethod != 0) и запуск кода только после этого? Это все равно приведет к сбою моего приложения из-за некоторой ошибки JNI.   -  person janonymous    schedule 01.04.2018


Ответы (1)


Символ [ используется для представления массивов (как если бы у вас было String[] msg), удалите его из своего метода GetStaticMethodID.

Вы можете просмотреть дополнительную информацию о типах и данных JNI. Структуры здесь

Также, как сказал Майкл, вы должны проверять исключения Java в своем собственном коде, потому что в противном случае ваше приложение выйдет из строя. Вы можете сделать это, используя функции исключения jni . Например:

jclass exClass = env->FindClass("some/random/class");
if (exClass == nullptr || env->ExceptionOccurred()) {
    env->ExceptionClear();
    // do smth in case of failure
}
person Dmitrii Z.    schedule 01.04.2018
comment
exClass будет иметь значение NULL, если класс/метод не существует или не может быть найден. По крайней мере, в моем случае. Так что если (exClass == NULL || ...) для меня. - person janonymous; 19.06.2018