本机崩溃:应用程序中JNI检测到错误:[线程]使用来自[线程]的JNIEnv *

时间:2018-06-29 10:01:45

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

以下是堆栈跟踪。导致崩溃的源代码是here

我跟踪了堆栈跟踪,直到android的源代码为here

我无法理解这意味着什么以及为什么它仅在某些时候发生。任何帮助,将不胜感激。很高兴分享更多细节。

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

import datetime
import datefinder as dt
from dateutil.parser import parse
from commonregex import CommonRegex


    class FindDates:

        def __init__(self):
            pass

        def isit_date(self, s):
            commonregex_object = CommonRegex(s)
            dates_list = commonregex_object.dates
            additional_dates = [x for x in s.split(" ") if len(x) == 8 and x.isdigit()]
            if len(additional_dates) > 0:
                additional_dates = [x[0:2]+"/"+x[2:4]+"/"+x[4:] for x in additional_dates]
                dates_list.extend(additional_dates)
            datefinder_dates = [w[1] for w in list(dt.find_dates(s, source=True))]
            dates_list.extend(datefinder_dates)
            return self.verify_dates(dates_list)

        @staticmethod
        def verify_dates(dates_list):
            dates_list = list(set(dates_list))
            min_year = datetime.datetime.now().year - 200
            max_year = min_year + 400
            return_date_list = []
            for each_date in dates_list:
                try:
                    dt_obj = parse(each_date)
                    if min_year <= dt_obj.year <= max_year:
                        return_date_list.append(each_date)
                except:
                    pass
            return return_date_list

1 个答案:

答案 0 :(得分:3)

似乎您的函数是从本机线程调用的,这会导致对FindClass和其他尝试与Java代码一起使用的JNI方法的调用崩溃

  

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调试:#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();,因为如果附加的本机线程退出而没有分离,则将导致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 函数查找自定义类。因此,在本机线程中(无论是否连接),在大多数情况下,对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;
      }
    }