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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】