env->FindClass() ClassNotFound

最近在做项目的时候遇到了一个类找不到的问题,相对于之前的解决方案还是有些不同,这里对问题进行记录

异常的检测和打印:

c++中Native代码调用JNI的时候如果产生了异常不会展开原生堆栈:
所以在c++JNI调用的时候构造一个FindClass时找不到类的异常,我们看到的实际的崩溃堆栈会是下面的样子,看不到c++层代码的调用链路:
在这里插入图片描述
在JNI调用的时候如果遇到了异常,不会立即抛出异常,当代码执行到受管理的代码时会抛出异常。

什么是受管理的代码:使用 Java 或 Kotlin 编程语言编写的代码

在进行异常检测的时候,我们可以使用ExceptionCheck或ExceptionOccurred方法,前者返回的是一个bool值,后者返回的是jthrowable,并没有直接获取jthrowable中异常message的方法,如果需要获取的话需要通过反射,自己在实践的时候使用下面的方法较为方便:

  	if (env->ExceptionCheck()) {
  		// 出现了异常,打印异常堆栈
        env->ExceptionDescribe();
    }
ClassNotFound

在进行JNI开发的时候,可能会遇到的一类问题就是FindClass找不到类,这里我们分两类讨论:

  1. 类被混淆,类名写错:这种自行检查,或者通过工具反编译查看类名称即可解决。
  2. 类找不到的时候查看崩溃的寻找类的path:例如下面的截图,path是 . ,表示当前是在系统类加载器中寻找我们要用的app中的类,这样的话肯定找不到。
    在这里插入图片描述
    解决方案(前三点参考官方给的解决方案):
  • 在 JNI_OnLoad 中执行一次 FindClass 查找,然后缓存类引用以供日后使用。在执行 JNI_OnLoad 过程中发出的任何 FindClass 调用都会使用与调用 System.loadLibrary 的函数关联的类加载器(这是一条特殊规则,用于更方便地进行库初始化)。如果您的应用代码要加载库,FindClass 会使用正确的类加载器。
  • 通过声明原生方法来获取 Class 参数,然后传入 Foo.class,从而将类的实例传递给需要它的函数。
  • 在某个便捷位置缓存对 ClassLoader 对象的引用,然后直接发出 loadClass 调用。这需要花费一些精力来完成。
  • env->GetObjectClass(),通过对象去获取其对应的类,

为什么FindClass会走到系统类加载器呢?查看实现:

art/runtime/jni/jni_internal.cc

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;
}

ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);

GetClassLoader方法最开始,先调用了上面的方法去获取method,这个方法具体做了什么呢?查看源码如下
art/runtime/thread.cc

ArtMethod* Thread::GetCurrentMethod(uint32_t* dex_pc_out,
                                    bool check_suspended,
                                    bool abort_on_error) const {
  // Note: this visitor may return with a method set, but dex_pc_ being DexFile:kDexNoIndex. This is
  //       so we don't abort in a special situation (thinlocked monitor) when dumping the Java
  //       stack.
  ArtMethod* method = nullptr;
  uint32_t dex_pc = dex::kDexNoIndex;
  StackVisitor::WalkStack(
      [&](const StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
        ArtMethod* m = visitor->GetMethod();
        if (m->IsRuntimeMethod()) {
          // Continue if this is a runtime method.
          return true;
        }
        method = m;
        dex_pc = visitor->GetDexPc(abort_on_error);
        return false;
      },
      const_cast<Thread*>(this),
      /* context= */ nullptr,
      StackVisitor::StackWalkKind::kIncludeInlinedFrames,
      check_suspended);

  if (dex_pc_out != nullptr) {
    *dex_pc_out = dex_pc;
  }
  return method;
}

通过上面的注释可以看到,上面的代码遍历Java Stack,寻找stack中的Java方法栈帧。
GetCurrentMethod返回后,在GetClassLoader方法中判断: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
https://cs.android.com/android/platform/superproject/+/master:art/runtime/jni/jni_internal.cc;l=2310?q=jni_internal.cc&ss=android%2Fplatform%2Fsuperproject

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值