Я хочу вызвать нативный код из java в случае нативной активности.

Предположим, у меня есть игровой движок в engine.so. Теперь я хочу добавить распознавание речи. Я добавил java-класс-оболочку и запустил распознавание речи из нативного кода через jni. Я хочу вернуть результат на родную сторону. Следуя примерам jni, я объявил собственный метод в классе java и вызываю его после завершения распознавания:

public native void onSpeechRecognized ( String value );

Я реализовал этот метод в engine.so. Конечно, я не загружаю engine.so с помощью System.loadLibrary, так как он уже загружен. Но код Java не видит реализацию метода, сообщая:

FATAL EXCEPTION: main
java.lang.UnsatisfiedLinkError: onSpeechRecognized
    at com.company.appname.SpeechRecognizerWrapper.onSpeechRecognized(Native Method)
    at com.company.appname.SpeechRecognizerWrapper$SpeechRecognitionListener.onResults(SpeechRecognizerWrapper.java:92)
    at android.speech.SpeechRecognizer$InternalListener$1.handleMessage(SpeechRecognizer.java:428)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:130)
    at android.app.ActivityThread.main(ActivityThread.java:3687)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
    at dalvik.system.NativeStart.main(Native Method)

Утилита nm показывает, что engine.so содержит Java_com_company_appname_SpeechRecognizerWrapper_onSpeechRecognized
Подпись создается с помощью javah.
Мой Android.mk

PROJ_PATH := $(call my-dir)
LIB_PATH := $(PROJ_PATH)/../../../../../Smart/Lib

include $(LIB_PATH)/Log/Projects/android/jni/Android-prebuilt.mk

...

LOCAL_PATH := $(PROJ_PATH)/../../../../../Smart/Smart
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := \
    $(PROJ_PATH)/../../../../../Smart \
    $(LIB_PATH)/Hash
LOCAL_MODULE    := smart
LOCAL_SRC_FILES := Animation/TextureAnimation.cpp
LOCAL_SRC_FILES += Base/Director.cpp

...

LOCAL_CFLAGS += -DNDEBUG -O3 -mcpu=cortex-a8 -mfpu=neon -ftree-vectorize -mvectorize-with-neon-quad -std=gnu++11
LOCAL_LDLIBS := -llog -landroid -lGLESv2 -lEGL -lOpenSLES
LOCAL_STATIC_LIBRARIES := android_native_app_glue Slb freetype Image FileSystem Noise Log Math Threads SharedPtr vmath png jpeg ScriptEngine QuestEngine Time tremolo

include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

Итак, почему java-сторона не видит нативную реализацию?

0
Sergei Ivanov 29 Янв 2015 в 23:44
Возможно, этот метод является статическим в вашем движке, поэтому вы должны объявить его как этот public static native void onSpeechRecognized (строковое значение);
 – 
Cristian Olaru
30 Янв 2015 в 01:27
Нет, это не статично JNIEXPORT void JNICALL Java_com_company_appname_SpeechRecognizerWrapper_onSpeechRecognized (JNIEnv *, jobject, jstring);
 – 
Sergei Ivanov
30 Янв 2015 в 02:30
Прочитайте здесь developer.android.com/training/articles/perf-jni. html#faq_ULE возможно вы сами найдете проблему
 – 
Cristian Olaru
30 Янв 2015 в 02:38
Спасибо за ссылку, но ничего не помогает. С такими решениями, как hello-jni, все ясно. Но ничего похожего на мой случай не нашел. Я не понимаю, где должна быть реализация - в отдельной библиотеке или там же, где находится android_main
 – 
Sergei Ivanov
30 Янв 2015 в 03:48
Извините, я не могу больше чем-то помочь, но я могу дать вам это видео, которое помогло мне, когда я начал использовать нативный код в своих приложениях youtube.com/…
 – 
Cristian Olaru
30 Янв 2015 в 04:12

2 ответа

Не знаю, почему класс-оболочка java не может видеть нативную функцию, но я решил эту проблему, просто используя RegisterNatives из нативного кода.

0
Sergei Ivanov 31 Янв 2015 в 13:53

Я заметил, что вы компилируете файлы .ccp, а не простой C. Чтобы jni мог автоматически найти функцию на основе имени функции (в отличие от регистрации в RegisterNatives), имя функции в объектном файле должно следовать определенной схеме.

Возможно ли, что вы не объявили функции «extern C», чтобы предотвратить искажение имен C++ (это то, что C++ делает для переименования функций в объектном файле, чтобы они включали информацию о типе, что приведет к тому, что jni не сможет найти потому что имя функции в объектном файле больше не соответствует правильному шаблону).

Если это так, вы можете решить проблему, окружив функции, которые вы хотите вызывать из JNI, с помощью:

extern "C" { <your function here> }

См., например, этот ответ SO:

UnsatisfiedLinkError для встроенной функции cpp в приложении для Android (ndk)

Ваш вывод из приведенной выше утилиты nm, похоже, не показывает всю строку nm, напечатанную для функции. Если по обе стороны от имени функции в необработанном выводе nm есть тарабарщина, это может указывать на то, что функция не является "extern C" и что искажение имени является источником ваших проблем.

0
Community 23 Май 2017 в 15:12