前言
前面讲ART虚拟机启动的时候说到,在init创建第一个横跨java和native的进程zygote的时候,会通过startVm创建虚拟机,VM创建完成后,会通过startReg完成虚拟机的JNI方法注册
startReg
// /frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// 此处ART虚拟机已经分析
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
// 创建jni
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
startVM就是创建虚拟机,紧接着在虚拟机创建完成后,调用了startReg
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
return -1;
}
}
调用register_jni_procs来注册jni方法,而这些方法就是存放在gRegJNI中
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) {
return -1;
}
}
return 0;
}
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_util_SeempLog),
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
...
}
REG_JNI是一个宏方法的定义
#define REG_JNI(name) { name, #name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
const char* mName;
};
可见,调用mProc就是调用其参数名指向的函数。比如第一个
REG_JNI(register_android_util_SeempLog)
// android_util_SeempLog.cpp
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "seemp_println_native", "(ILjava/lang/String;)I",
(void*) android_util_SeempLog_println_native },
};
int register_android_util_SeempLog(JNIEnv* env)
{
jclass clazz = env->FindClass("android/util/SeempLog");
if (clazz == NULL) {
return -1;
}
return AndroidRuntime::registerNativeMethods(env, "android/util/SeempLog", gMethods,
NELEM(gMethods));
}
调用了registerNativeMethods,而传入的参数是gMethods,也就是java层函数名和jni层函数的映射关系
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
// JNIHelp.cpp
MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
int result = e->RegisterNatives(c.get(), gMethods, numMethods);
}
// jni.h
jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,
jint nMethods) {
return functions->RegisterNatives(this,clazz,methods,nMethods);
}
unctions是指向JNINativeInterface结构体指针,该过程完成了java层方法到native层方法的映射。
loadlibrary
而我们平时正常使用jni的时候,都是需要调用loadLibrary来加载so库,那就从这里开始jni的注册流程
// System.java
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
// Runtime.java
void loadLibrary0(Class<?> fromClass, String libname) {
ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);
loadLibrary0(classLoader, fromClass, libname);
}
// This overload exists for @UnsupportedAppUsage
void loadLibrary0(ClassLoader loader, String libname) {
// Pass null for callerClass, we don't know it at this point. Passing null preserved
// the behavior when we used to not pass the class.
loadLibrary0(loader, null, libname);
}
private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {
String filename = System.mapLibraryName(libraryName);
String error = nativeLoad(filename, loader, callerClass);
}
private static String nativeLoad(String filename, ClassLoader loader) {
return nativeLoad(filename, loader, null);
}
private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
// Runtime.c
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jclass caller)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
}
// openjdkjvm.cc
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jclass caller) {
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
caller,
&error_msg);
}
// java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jclass caller_class,
std::string* error_msg) {
// 通过dlopen的方式打开so库
void* handle = android::OpenNativeLibrary(
env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
(caller_location.empty() ? nullptr : caller_location.c_str()),
library_path.get(),
&needs_native_bridge,
&nativeloader_error_msg);
// 通过dlsystem的方式找到JNI_OnLoad方法
void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
using JNI_OnLoadFn = int(*)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
}
// /system/core/libnativeloader/native_loader.cpp
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
jobject class_loader, const char* caller_location, jstring library_path,
bool* needs_native_bridge, char** error_msg) {
void* handle = dlopen(path, RTLD_NOW);
}
以上其实就是一个so库加载的过程了,通过JNI_OnLoad方法,而jnionload方法中,多是会调用registerNativeMethods,也就是上面说到了,所以殊途同归,
JNI的动态注册和静态注册
动态注册
动态注册也就是我们上面说到的,通过实现JNI_OnLoad方法,来主动建立java层函数和native函数的映射关系,这样逻辑清晰,并且不会有效率的损失。
静态注册
静态注册也就是我们平常使用的通过javah来生成的文件,这种方式程序运行效率低,因为初次调用natvie函数是需要根据函数名在jni层是搜索对应的本地函数(通过JNIEXPORT和JNICALL宏实现),然后监理对应的关系,这个过程会比较耗时。