静态注册
对于静态注册的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。
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;
}
动态注册通过
jni多线程与类查询
通过java层调用的jni函数,其与此java函数属于同一个java线程。每一个java线程都有一个JNIEnv线程环境块,而每一个JNIEnv都对应者一个classloader。对于java函数调用的jni函数而言,其对应的classloader就是加载此so时调用System.load调用者的classloader。再通过JNIEnv提供的接口FindClass去查询相关类时使用的就是此JNIEnv对应的classloader。
而对于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)
标签:jni,调用,java,函数,注册,android,method,native
From: https://www.cnblogs.com/revercc/p/17043309.html