android jni静态注册和动态注册

静态注册

对于静态注册的jni函数而言,jni函数签名名称要与java层对应的函数名称一一对应。当一个java类被加载时会调用LoadMethod将其所有的方法也都加载到虚拟机中,并调用LinkMethod设置函数的入口。

static void LinkCode(ClassLinker* class_linker,
                     ArtMethod* method,
                     const OatFile::OatClass* oat_class,
                     uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {
//省略
if (method->IsNative()) {
    // Unregistering restores the dlsym lookup stub.
    //设置native函数入口为一个统一的跳板函数
    method->UnregisterNative();
    if (enter_interpreter || quick_code == nullptr) {
      // We have a native method here without code. Then it should have either the generic JNI
      // trampoline as entrypoint (non-static), or the resolution trampoline (static).
      // TODO: this doesn't handle all the cases where trampolines may be installed.
      const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
      DCHECK(class_linker->IsQuickGenericJniStub(entry_point) ||
             class_linker->IsQuickResolutionStub(entry_point));
    }
  }
}

UnregisterNative函数调用GetJniDlsymLookupStup函数返回的是跳板函数art_jni_dlsym_lookup_stub的地址,然后调用SetEntryPointFromJni设置ArtMethod的data_为art_jni_dlsym_lookup_stub。

void ArtMethod::UnregisterNative() {
  CHECK(IsNative()) << PrettyMethod();
  // restore stub to lookup native pointer via dlsym
  SetEntryPointFromJni(GetJniDlsymLookupStub());
}

art_jni_dlsym_lookup_stub函数再调用dlsym通过签名规则匹配到对应的native函数,然后设置ArtMethod的native函数入口data_并进行调用。

动态注册

Jni_Onload中调用RegisterNative注册jni函数,其内部会调用ArtMethod::RegisterNative并将ArtMethod的native函数入口data_设置为对应的动态注册的函数。因此动态注册对与函数名称没有要求,而且因为调用的时候不需要进行函数签名规则的匹配,省去了函数查找所需要的时间所以效率更高。

const void* ArtMethod::RegisterNative(const void* native_method) {
  CHECK(IsNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();
  void* new_native_method = nullptr;
  Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
                                                                  native_method,
                                                                  /*out*/&new_native_method);
  //设置ArtMethod的data_为new_native_method
  SetEntryPointFromJni(new_native_method);
  return new_native_method;
}

native多线程与类查询

通过java层调用的jni函数,其与此java函数属于同一个java线程。每一个java线程都有一个JNIEnv线程环境块,而每一个JNIEnv都对应者一个classloader。对于java函数调用的jni函数而言,其对应的classloader就是加载此so时调用System.load调用者的classloader。再通过JNIEnv提供的接口FindClass去查询相关类时使用的就是此JNIEnv对应的classloader。

  ScopedJavaLocalRef<jclass> FindClass(JNIEnv* env, const char* c_name) {
    // ClassLoader.loadClass expects a classname with components separated by
    // dots instead of the slashes that JNIEnv::FindClass expects.
    std::string name(c_name);
    std::replace(name.begin(), name.end(), '/', '.');
    ScopedJavaLocalRef<jstring> j_name = NativeToJavaString(env, name);
    const jclass clazz = static_cast<jclass>(env->CallObjectMethod(
        class_loader_.obj(), load_class_method_, j_name.obj()));
    CHECK_EXCEPTION(env);
    return ScopedJavaLocalRef<jclass>(env, clazz);
  }

而对于native多线程而言,其他线程默认情况下并不与android虚拟机相关联,自然也没有与之对应的java线程。可以通过手动调用JavaVM的AttachCurrentThread方法将当前native线程与android虚拟机相关联并获取对应的java线程的JNIEnv,但是此JNIEnv默认对应的classloader为系统类加载器(getSystemClassLoader),所以如果通过此JNIEnv去查询app中自定义的类会出现如下错误:搜索不到。

java.lang.ClassNotFoundException: Didn't find class "class_name" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]
       at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
       at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
       at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
posted @ 2023-01-11 11:46  怎么可以吃突突  阅读(399)  评论(0编辑  收藏  举报