java - native 崩溃 : JNI DETECTED ERROR IN APPLICATION: [thread] using JNIEnv* from [thread]

标签 java android java-native-interface android-runtime

以下是堆栈跟踪。崩溃起源的源代码是here .

我跟踪堆栈跟踪直到 android 的源代码是 here .

我无法理解这意味着什么以及为什么它只是有时发生。任何帮助,将不胜感激。很高兴分享更多详细信息。

我们已经能够在 Android 7.0 设备上重现此崩溃。但它并不一致。

06-28 19:09:26.147  5696  5696 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-28 19:09:26.147  5696  5696 F DEBUG   : Native Crash TIME: 265472
06-28 19:09:26.147  5696  5696 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-28 19:09:26.148  5696  5696 F DEBUG   : Build fingerprint: 'Karbonn/K9_Smart_Eco/K9_Smart_Eco:7.0/NRD90M/1498048597:user/release-keys'
06-28 19:09:26.148  5696  5696 F DEBUG   : Revision: '0'
06-28 19:09:26.148  5696  5696 F DEBUG   : ABI: 'arm'
06-28 19:09:26.148  5696  5696 F DEBUG   : pid: 5587, tid: 5689, name: JS Thread  >>> com.hashcube.sqmtest <<<
06-28 19:09:26.149  5696  5696 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
06-28 19:09:26.152  5696  5696 F DEBUG   : Abort message: 'art/runtime/java_vm_ext.cc:470] JNI DETECTED ERROR IN APPLICATION: thread Thread[54,tid=5689,Native,Thread*=0x8a4de500,peer=0x12dc89d0,"JS Thread"] using JNIEnv* from thread Thread[54,tid=5689,Native,Thread*=0x8a4de500,peer=0x12dc89d0,"JS Thread"]'
06-28 19:09:26.152  5696  5696 F DEBUG   :     r0 00000000  r1 00001639  r2 00000006  r3 00000008
06-28 19:09:26.152  5696  5696 F DEBUG   :     r4 899ff978  r5 00000006  r6 899ff920  r7 0000010c
06-28 19:09:26.152  5696  5696 F DEBUG   :     r8 00000000  r9 add696a4  sl 00000ac4  fp add2eecf
06-28 19:09:26.153  5696  5696 F DEBUG   :     ip 0000000b  sp 899feb50  lr b039a597  pc b039cdf4  cpsr 20070010
06-28 19:09:26.192  5696  5696 F DEBUG   :
06-28 19:09:26.192  5696  5696 F DEBUG   : backtrace:
06-28 19:09:26.193  5696  5696 F DEBUG   :     #00 pc 00049df4  /system/lib/libc.so (tgkill+12)
06-28 19:09:26.193  5696  5696 F DEBUG   :     #01 pc 00047593  /system/lib/libc.so (pthread_kill+34)
06-28 19:09:26.193  5696  5696 F DEBUG   :     #02 pc 0001d855  /system/lib/libc.so (raise+10)
06-28 19:09:26.193  5696  5696 F DEBUG   :     #03 pc 000193a1  /system/lib/libc.so (__libc_android_abort+34)
06-28 19:09:26.193  5696  5696 F DEBUG   :     #04 pc 00017014  /system/lib/libc.so (abort+4)
06-28 19:09:26.193  5696  5696 F DEBUG   :     #05 pc 003188f5  /system/lib/libart.so (_ZN3art7Runtime5AbortEv+252)
06-28 19:09:26.194  5696  5696 F DEBUG   :     #06 pc 000b4e79  /system/lib/libart.so (_ZN3art10LogMessageD2Ev+864)
06-28 19:09:26.194  5696  5696 F DEBUG   :     #07 pc 00238971  /system/lib/libart.so (_ZN3art9JavaVMExt8JniAbortEPKcS2_+1664)
06-28 19:09:26.194  5696  5696 F DEBUG   :     #08 pc 00238b63  /system/lib/libart.so (_ZN3art9JavaVMExt9JniAbortVEPKcS2_St9__va_list+58)
06-28 19:09:26.194  5696  5696 F DEBUG   :     #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)
06-28 19:09:26.194  5696  5696 F DEBUG   :     #11 pc 000c941f  /system/lib/libart.so (_ZN3art11ScopedCheck22CheckPossibleHeapValueERNS_18ScopedObjectAccessEcNS_12JniValueTypeE+26)
06-28 19:09:26.195  5696  5696 F DEBUG   :     #12 pc 000c88fb  /system/lib/libart.so (_ZN3art11ScopedCheck5CheckERNS_18ScopedObjectAccessEbPKcPNS_12JniValueTypeE+802)
06-28 19:09:26.195  5696  5696 F DEBUG   :     #13 pc 000cdd79  /system/lib/libart.so (_ZN3art8CheckJNI8GetFieldEPKcP7_JNIEnvP8_jobjectP9_jfieldIDbNS_9Primitive4TypeE+496)
06-28 19:09:26.195  5696  5696 F DEBUG   :     #14 pc 000c2eef  /system/lib/libart.so (_ZN3art8CheckJNI11GetIntFieldEP7_JNIEnvP8_jobjectP9_jfieldID+42)
06-28 19:09:26.195  5696  5696 F DEBUG   :     #15 pc 0009a35c  /data/app/com.hashcube.sqmtest-1/lib/arm/libtealeaf.so (_Z19navigator_info_initv+252)
06-28 19:09:26.195  5696  5696 F DEBUG   :     #16 pc 00084ca0  /data/app/com.hashcube.sqmtest-1/lib/arm/libtealeaf.so (_Z25js_navigator_get_templatev+100)
06-28 19:09:26.196  5696  5696 F DEBUG   :     #17 pc 0007caf0  /data/app/com.hashcube.sqmtest-1/lib/arm/libtealeaf.so (init_js+740)
06-28 19:09:26.196  5696  5696 F DEBUG   :     #18 pc 00089dc4  /data/app/com.hashcube.sqmtest-1/lib/arm/libtealeaf.so (core_init_js+36)
06-28 19:09:26.196  5696  5696 F DEBUG   :     #19 pc 00099a28  /data/app/com.hashcube.sqmtest-1/lib/arm/libtealeaf.so (Java_com_tealeaf_NativeShim_initJS+180)
06-28 19:09:26.196  5696  5696 F DEBUG   :     #20 pc 0088494d  /data/app/com.hashcube.sqmtest-1/oat/arm/base.odex (offset 0x82e000)

最佳答案

看起来你的函数是从 native 线程调用的,它导致调用 FindClass 和其他试图使用 java 代码的 JNI 方法崩溃

06-28 19:09:26.194 5696 5696 F DEBUG : #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); 行中,您尝试使用空 JNIEnv 指针将当前线程附加到 JVM。您已经声明了它,但从未分配过。我在您的文件中寻找 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();,因为如果附加的 native 线程在没有分离的情况下退出,将导致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...
       ...
    });

接下来需要注意的是,如果调用堆栈未在您的 Java 代码中启动,则您不能使用 FindClass 函数来查找自定义类。因此,在大多数情况下,在 native 线程中(无论是否附加)调用 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;
      }
    }

关于java - native 崩溃 : JNI DETECTED ERROR IN APPLICATION: [thread] using JNIEnv* from [thread],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51099200/

相关文章:

java - 在 JFrame 中使用坐标平面

java - 如何对javamail中的日语字符进行编码

java - 无法使用有界通配符传递通用子类的内部类

android - 我已经配对的蓝牙设备是否必须处于可发现模式才能扫描和查看?

Android手机应用 Intent

android - 将 void* 转换为字节数组

android - 是否可以从 native 代码获取 Android Activity 堆栈?

java - 通过 STS eclipse 连接到 ClearCase(CCRC) 时出错

android - 为什么 volley 库不能在 android 9(API 28) 上工作,而在 android 8(API 27) 上工作正常?

c++ - 如何使用 JNI 从 Java 方法将字符串值导入 C++