"Не найдена реализация для" с использованием NativeActivity
У меня есть проект, над которым я работаю, который использует NativeActivity вместе с файлами android_native_app_glue для подключения к стороне Java. Мне нужно получить доступ к нескольким классам, отличным от NDK, через Java и выяснить, как делать вызовы из C в Java, однако у меня возникают проблемы с обратным. Когда я звоню из Java в CI и получаю сообщение "Не найдена реализация для и т. Д.", Я проверил большинство ловушек для такого рода вызовов и считаю, что все правильно. Вот класс Activity.
package com.JNITest;
import android.app.NativeActivity;
import android.util.Log;
public class JniNativeActivity extends NativeActivity
{
private static String TAG = "JniNativeActivity";
public static native void nativeJniCall();
public JniNativeActivity()
{
super();
Log.v(TAG, "Creating JniNativeActivity");
}
public void testCall()
{
Log.v(TAG, "Calling native");
nativeJniCall();
}
}
Поскольку это NativeActivity, использующий клейкий код, JNI_OnLoad не вызывается, точка входа - android_main (который клейкий код настроил для запуска в своем собственном потоке).
void android_main(struct android_app* state)
{
test_call(state->activity);
etc...
}
...
void test_call(ANativeActivity *activity)
{
JNIEnv *jni;
activity->vm->AttachCurrentThread(&jni, NULL);
jclass activityClass=jni->GetObjectClass(activity->clazz);
jmethodID testCallId=jni->GetMethodID(activityClass, "testCall", "()V");
jni->CallVoidMethod(activity->clazz, testCallId);
}
extern "C"
{
JNIEXPORT void JNICALL Java_com_JNITest_JniNativeActivity_nativeJniCall(JNIEnv *, jclass)
{
LOGI("nativeJniCall");
}
}
из logcat
...
waiting for debugger to settle...
waiting for debugger to settle...
debugger has settled (1388)
Creating JniNativeActivity
Creating: 0xb420b100
Config: mcc=0 mnc=0 lang=en cnt=US orien=2 touch=3 dens=576 keys=2 nav=1 keysHid=1 navHid=0 sdk=22 size=2 long=1 modetype=1 modenight=1
Start: 0xb420b100
removeObsoleteFile: deleting file=161_task.xml
Calling native
No implementation found for void com.JNITest.JniNativeActivity.nativeJniCall() (tried Java_com_JNITest_JniNativeActivity_nativeJniCall and Java_com_JNITest_JniNativeActivity_nativeJniCall__)
activityState=10
Resume: 0xb420b100
activityState=11
Adding window Window{2b21e24c u0 com.JNITest/com.JNITest.JniNativeActivity} at 2 of 8 (before Window{3238fdec u0 Starting com.JNITest})
InputQueueCreated: 0xb420b100 -- 0xb420b880
APP_CMD_INPUT_CHANGED
Attaching input queue to looper
NativeWindowCreated: 0xb420b100 -- 0xb417ae08
APP_CMD_INIT_WINDOW
Waiting for host to establish connection
WindowFocusChanged: 0xb420b100 -- 1
Accepting new connection from local client
Accepted new client connection: PID = 3945, fd = 39
Requesting connection from host
Sending initiation request to host
Accepting new connection from host service
Accepted new host connection: fd = 40
Connecting local socket 39 to host socket 40
HostConnection::get() New Host Connection established 0xb4272e50, tid 3976
Displayed com.JNITest/.JniNativeActivity: +5s618ms
Тот факт, что что-либо запускается, подразумевает, что библиотека загружена, она также находится в журнале
...
=thread-created,id="9",group-id="i1"[New Thread 3958]
=thread-created,id="10",group-id="i1"[New Thread 3959]
=thread-created,id="11",group-id="i1"[New Thread 3960]
Loaded 'D:\projects\JNITest\JNITest\JNITest.Android.NativeActivity\x86\Debug\libJNITest.so'
=thread-created,id="12",group-id="i1"[New Thread 3976]
[Switching to Thread 3976]
=thread-created,id="13",group-id="i1"[New Thread 3975]
Loaded 'gralloc.donatello.so'
...
Я проверил его имя с Джава
extern "C" {
#endif
/*
* Class: com_JNITest_JniNativeActivity
* Method: nativeJniCall
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_JNITest_JniNativeActivity_nativeJniCall
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
Я также проверил библиотеку externs и функция там
...
U glRotatef
U glShadeModel
U glTranslatef
U glVertexPointer
U glViewport
00007004 D indices
00004b70 T Java_com_JNITest_JniNativeActivity_nativeJniCall
U malloc
U memcpy
U memset
U pipe
...
У меня была эта проблема в другом проекте, и я создал тестовое приложение (откуда взят код выше), так что это не случайная уникальная проблема конфигурации. Я немного растерялся с этим, я предполагаю, что у него есть некоторые проблемы с NativeActivity и клейким кодом (вероятно, с многопоточностью), но я не уверен, как обойти это. Любая помощь будет оценена.
1 ответ
Хорошо, я нашел проблему. Кажется, что в этом случае, поскольку System.loadlibrary не используется со связующим кодом, тогда стандартный механизм поиска не работает. Вы должны вызвать RegisterNatives вручную (из действительного JNIEnv). Так что для этого случая вам нужно сделать следующее:
void nativeJniCall(JNIEnv *, jclass)
{
LOGV("nativeJniCall");
}
void android_main(struct android_app* state)
{
test_call(state->activity);
etc...
}
...
void test_call(ANativeActivity *activity)
{
JNIEnv *jni;
activity->vm->AttachCurrentThread(&jni, NULL);
jclass activityClass=jni->GetObjectClass(activity->clazz);
jmethodID testCallId=jni->GetMethodID(activityClass, "testCall", "()V");
JNINativeMethod methodTable[]=
{
{"nativeJniCall", "()V", (void *)nativeJniCall}
};
int methodTableSize=sizeof(methodTable)/sizeof(methodTable[0]);
jni->RegisterNatives(activityClass, methodTable, methodTableSize);
jni->CallVoidMethod(activity->clazz, testCallId);
}
Для производства вы должны кэшировать все jclass/jmethoidID (не забывайте использовать NewGlobalRef/DeleteGlobalRef) и регистрировать только собственные функции в классе один раз, но это служит примером. Надеюсь, это сэкономит кому-то еще время.