异常的检测和打印:
c++中Native代码调用JNI的时候如果产生了异常不会展开原生堆栈:
所以在c++JNI调用的时候构造一个FindClass时找不到类的异常,我们看到的实际的崩溃堆栈会是下面的样子,看不到c++层代码的调用链路:
在JNI调用的时候如果遇到了异常,不会立即抛出异常,当代码执行到受管理的代码时会抛出异常。
什么是受管理的代码:使用 Java 或 Kotlin 编程语言编写的代码
在进行异常检测的时候,我们可以使用ExceptionCheck或ExceptionOccurred方法,前者返回的是一个bool值,后者返回的是jthrowable,并没有直接获取jthrowable中异常message的方法,如果需要获取的话需要通过反射,自己在实践的时候使用下面的方法较为方便:
if (env->ExceptionCheck()) {
// 出现了异常,打印异常堆栈
env->ExceptionDescribe();
}
ClassNotFound
在进行JNI开发的时候,可能会遇到的一类问题就是FindClass找不到类,这里我们分两类讨论:
- 类被混淆,类名写错:这种自行检查,或者通过工具反编译查看类名称即可解决。
- 类找不到的时候查看崩溃的寻找类的path:例如下面的截图,path是 . ,表示当前是在系统类加载器中寻找我们要用的app中的类,这样的话肯定找不到。
解决方案(前三点参考官方给的解决方案):
- 在 JNI_OnLoad 中执行一次 FindClass 查找,然后缓存类引用以供日后使用。在执行 JNI_OnLoad 过程中发出的任何 FindClass 调用都会使用与调用 System.loadLibrary 的函数关联的类加载器(这是一条特殊规则,用于更方便地进行库初始化)。如果您的应用代码要加载库,FindClass 会使用正确的类加载器。
- 通过声明原生方法来获取 Class 参数,然后传入 Foo.class,从而将类的实例传递给需要它的函数。
- 在某个便捷位置缓存对 ClassLoader 对象的引用,然后直接发出 loadClass 调用。这需要花费一些精力来完成。
- env->GetObjectClass(),通过对象去获取其对应的类,
为什么FindClass会走到系统类加载器呢?查看实现:
static ObjPtr<mirror::ClassLoader> GetClassLoader(const ScopedObjectAccess& soa)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
// If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
if (method == jni::DecodeArtMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
return soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
}
// If we have a method, use its ClassLoader for context.
if (method != nullptr) {
return method->GetDeclaringClass()->GetClassLoader();
}
// We don't have a method, so try to use the system ClassLoader.
ObjPtr<mirror::ClassLoader> class_loader =
soa.Decode<mirror::ClassLoader>(Runtime::Current()->GetSystemClassLoader());
if (class_loader != nullptr) {
return class_loader;
}
// See if the override ClassLoader is set for gtests.
class_loader = soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
if (class_loader != nullptr) {
// If so, CommonCompilerTest should have marked the runtime as a compiler not compiling an
// image.
CHECK(Runtime::Current()->IsAotCompiler());
CHECK(!Runtime::Current()->IsCompilingBootImage());
return class_loader;
}
// Use the BOOTCLASSPATH.
return nullptr;
}
上面的if (method != nullptr) { 判断是否有Java堆栈帧,没有的话就走到下面的GetSystemClassLoader得到系统的类加载器,因此就找不到app中的类,当我们使用从c++层的线程去执行FindClass的时候会遇到上述问题,大家可以参考上面的解决方案尝试解决。
参考:
https://blog.csdn.net/u013989732/article/details/80707607
https://developer.android.com/training/articles/perf-jni?hl=zh-cn#faq:-why-didnt-findclass-find-my-class