Похоже, что ваша функция вызывается из собственного потока, и это приводит к сбоям вызова FindClass и других методов JNI, которые пытаются работать с кодом Java
06-28 19: 09: 26,194 5696 5696 F ОТЛАДКА: # 09 pc 000ca81b /system/lib/libart.so (_ZN3art11ScopedCheck6AbortFEPKcz + 46)
06-28 19: 09: 26,194 5696 5696 F DEBUG: # 10 pc 000ca305 /system/lib/libart.so (_ZN3art11ScopedCheck11CheckThreadEP7_JNIEnv + 104)
В вашем коде в файле jni / platform / native_shim.cpp я вижу:
static JNIEnv* get_env() {
JNIEnv* env;
static_vm->AttachCurrentThread(&env, NULL);
return env;
}
native_shim *get_native_shim() {
if(shim.instance == NULL) {
LOG("{native} ERROR: Tried to get native shim when there wasn't one");
#if DEBUG
*((int*)0) = -1;
#else
exit(1);
#endif
}
shim.env = get_env();
return &shim;
}
В строке static_vm->AttachCurrentThread(&env, NULL);
вы пытаетесь присоединить текущий поток к JVM с нулевым указателем JNIEnv. Вы объявили это, но никогда не назначали. Я искал функцию JNI_OnLoad в ваших файлах, но не нашел ее. Рекомендуется один раз получить JavaVM в этом методе и сохранить его где-нибудь, чтобы вы могли получить указатель JNIEnv из него там, где вам нужно. Это может быть функция, подобная следующей:
JavaVM *java_machine;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
java_machine = vm;
}
int get_env(JNIEnv **g_env) {
int getEnvStat = java_machine->GetEnv((void **) g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
if (java_machine->AttachCurrentThread(g_env, nullptr) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "GetEnvironmentRoutine", "FAILED ATTACH THREAD");
return 2; //Failed to attach
}
return 1; //Attached. Need detach
}
return 0;//Already attached
}
И вы должны вызвать java_machine->DetachCurrentThread();
в конце метода, потому что, если присоединенный собственный поток завершает работу без отсоединения, это вызовет сбой java-машины.
Вы также можете написать оболочку RAII, чтобы быть уверенным, что ваш поток отсоединен от всех ветвей метода.
class attached_env final {
public:
attached_env() {
auto resCode = get_env(&mEnv);
if (resCode == 2)
throw std::runtime_error("Cannot retrieve JNI environment");
needDetach = (resCode == 1);
}
~attached_env() {
if (needDetach) {
java_machine->DetachCurrentThread();
}
}
JNIEnv *env() const noexcept {
return mEnv;
}
private:
JNIEnv *mEnv;
bool needDetach;
};
template<typename Callable>
auto call_in_attached_thread(Callable func) {
attached_env env;
return func(env.env());
}
Таким образом, ваш метод может выглядеть следующим образом (необходимо уточнить, см. Ниже):
navigator_info* navigator_info_init() {
call_in_attached_thread([=](auto env) {
jclass display_metrics_class = (jclass)env->FindClass("android/util/DisplayMetrics");//WILL NOT WORK! SEE BELOW
jfieldID density_dpi = env->GetFieldID(display_metrics_class, "densityDpi", "I");
jfieldID xdpi = env->GetFieldID(display_metrics_class, "xdpi", "F");
//And so on...
...
});
Следующее, что необходимо принять во внимание, это то, что вы не можете использовать функцию FindClass для поиска пользовательских классов, если в вашем Java-коде не запущен стек вызовов. Таким образом, в собственных потоках (независимо от того, присоединены они или нет) вызов FindClass в большинстве случаев приведет к сбою. Вам нужно найти классы в JNI_OnLoad и сохранить их в глобальных переменных, используя глобальные ссылки на Java:
jclass globalDisplayMetricsClassRef;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
//
//previous code here
//
auto localRef = env->FindClass("android/util/DisplayMetrics");
globalDisplayMetricsClassRef = (jclass)env->NewGlobalRef(localRef);
}
Итак, наконец мы получаем:
navigator_info* navigator_info_init() {
call_in_attached_thread([=](auto env) {
jfieldID density_dpi = env->GetFieldID(globalDisplayMetricsClassRef, "densityDpi", "I");
jfieldID xdpi = env->GetFieldID(display_metrics_class, "xdpi", "F");
//And so on...
...
});
ОБНОВЛЕНИЕ:
Кусок кода из функции ART CheckThread
// Verify that the current thread is (a) attached and (b) associated with
// this particular instance of JNIEnv.
if (soa_.Env() != threadEnv) {
if (soa_.Vm()->work_around_app_jni_bugs) {
// If we're keeping broken code limping along, we need to suppress the abort...
LOG(ERROR) << "APP BUG DETECTED: thread " << *self << " using JNIEnv* from thread " << *soa_.Self();
} else {
JniAbortF(function_name_, "thread %s using JNIEnv* from thread %s",
ToStr<Thread>(*self).c_str(), ToStr<Thread>(*soa_.Self()).c_str());
return;
}
}