在之前的文章中,我们熟悉了JNI的代码实现,并尝试手写了一个JNI的demo,实现了Java与cpp层的沟通。但是如果仔细思考还是会有一些困惑,Java和cpp最开始究竟是如何找到对方的?那种方法的映射在系统层面是如何建立起来的。这些问题是JNI规范文档所不能告诉我们的,需要我们自己找到答案。而答案就在源码里。
JNI的背景知识补充
JNI是Java语言定义的与cpp进行沟通的接口,接口具体由不同的虚拟来实现。关于JNI有以下几个比较重要的类型:
- JavaVM:表示Java虚拟机,进程相关
- JNIEnv:表示JNI环境的上下文,例如注册、查找类、异常等。 线程相关
- jclass:在JNI中表示对应的的Java类。
- jmethodID:在JNI中表示对应的的Java类中的方法的id。
- jfiledID:在JNI中表示对应的Java类中的属性的id。
- thread:JNI中通过AttachCurrentThread和DetachCurrentThread方法,实现和Java线程的结合。
在Android中,JNI的接口声明在系统的libnativehelper.so的动态库中,相关源码在libnativehelper/include_jni/jni.h。而JNI的实现则在虚拟机中,比如JNI中的函数操作表的实现就在art的art/runtime/jni/jni_internal.cc中。
cpp与Java的初次联系
我们假设一个时间点,在这个时间点之前,Java与cpp没有任何联系,而在这个时间点之后,他们都知道了对方的存在,建立起了沟通机制。
这个时间点cpp代码会第一次创建JVM虚拟机,创建JNIEnv上下文环境,同时导入JNI的操作函数表,并调用Java层的一个入口方法。这个时间点就是Android开机启动过程中的zygote进程的启动。
从zygote看起
我们就从zygote进程的入口函数开始看起。
c
// frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
...
...
// 初始化AppRuntime AppRuntime继承了AndroidRuntime
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
...
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
// zygote进程
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
...
...
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) { // zygote进程,传入的com.android.internal.os.ZygoteInit是Java类
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (!className.isEmpty()) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
这是zygote进程的初始化程序,我们看到的AppRuntime继承自AndroidRuntime:
看一下AndroidRuntime的start方法
arduino
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// 进入start方法主要办了三件事
...
...
// 1 加载本地库,创建虚拟机,导入JNI函数表
// 初始化JNI的函数操作,
JniInvocation jni_invocation;
// 就是加载系统默认的so库并从中导入相关的接口
jni_invocation.Init(NULL);
JNIEnv* env; // 定义一个JNIEnv指针
//
if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
return;
}
// 虚拟机创建后的回调方法
onVmCreated(env);
// 2,通过JNI 动态注册Android中已有的Java native 函数
// 开始注册Android已有的Java native方法
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
// 3 调用com.android.internal.os.ZygoteInit.main() 这个静态方法入口,从cpp进入Java层
char* slashClassName = toSlashClassName(className != NULL ? className : "");
// 找到java classname:com.android.internal.os.ZygoteInit
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
...
} else {
// 找到com.android.internal.os.ZygoteInit里面的main方法
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
...
} else {
//调用mian方法入口,进入Java层
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
}
}
如代码注释所述,start方法主要做了三件事:
-
- 创建虚拟机,配置JNI函数
-
- 使用JNI动态注册Android系统已有的native方法
-
- 调用com.android.internal.os.ZygoteInit的main入口,进入Java层
创建虚拟机
cpp
/*********** frameworks/base/core/jni/AndroidRuntime.cpp ********************/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
JavaVMInitArgs initArgs;
...
...
//一些虚拟机的配置
...
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
// JNI_CreateJavaVM 创建Java虚拟机
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
}
/*********** art/runtime/jni/java_vm_ext.cc ********************/
// art虚拟机实现
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
// 判断下版本号,只能是是JNI_VERSION_1_2 JNI_VERSION_1_4 JNI_VERSION_1_6,不能瞎传
if (JavaVMExt::IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
return JNI_EVERSION;
}
bool ignore_unrecognized = args->ignoreUnrecognized;
// 创建 Runtime 具体逻辑见下面的函数 Runtime::Create
if (!Runtime::Create(options, ignore_unrecognized)) {
return JNI_ERR;
}
//把Android系统的默认库都加载进内存中
android::InitializeNativeLoader();
// 获取刚才创建的Runtime对象
Runtime* runtime = Runtime::Current();
// 调用start方法,注册runtime相关的jni方法,以及设置一些基础环境
// 具体逻辑见下面的Runtime::Start函数
bool started = runtime->Start();
if (!started) {
delete Thread::Current()->GetJniEnv();
delete runtime->GetJavaVM();
LOG(WARNING) << "CreateJavaVM failed";
return JNI_ERR;
}
// p_env指针指向 当前线程中获取JNIEnv
*p_env = Thread::Current()->GetJniEnv();
// p_vm指针指向 Runtime中获取的JavaVM
// 此时Javavm已经创建成功
*p_vm = runtime->GetJavaVM();
return JNI_OK;
}
/*********** art/runtime/runtime.cc ********************/
/************************创建 Runtime start *********************************/
bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
RuntimeArgumentMap runtime_options;
// 调用Create()函数
return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&
Create(std::move(runtime_options));
}
// 创建Runtime
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
if (Runtime::instance_ != nullptr) {
return false;
}
// Runtime类很大,就不列举了,有兴趣可以去看头文件art/runtime/runtime.h
instance_ = new Runtime; // 手动申请内存空间,创建Runtime对象
Locks::SetClientCallback(IsSafeToCallAbort);
// 在init逻辑里创建JavaVM的对象
if (!instance_->Init(std::move(runtime_options))) {
...
return false;
}
return true;
}
// 创建Java虚拟机
void Runtime::Init(){
...
...
java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg);
if (java_vm_.get() == nullptr) {
LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg;
return false;
}
// 把一个获取与当前线程绑定的JNIEnv对象 的钩子函数 保存起来,
// 便于以后直接通过java_vm->GetEnv()接口来获取对应的JNIEnv
java_vm_->AddEnvironmentHook(JNIEnvExt::GetEnvHandler);
...
...
}
// GetEnvHandler 钩子函数
jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
if (JavaVMExt::IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
return JNI_EVERSION;
}
Thread* thread = Thread::Current();
*env = thread->GetJniEnv();
return JNI_OK;
}
/************************创建 Runtime end *********************************/
/************************创建 Runtime::strat start *********************************/
// runtime::start方法
// 主要注册
bool Runtime::Start() {
...
...
// 获取当前线程
Thread* self = Thread::Current();
started_ = true;
// JNI动态注册Runtime相关的native方法
// 具体函数逻辑见下面的RegisterRuntimeNativeMethods函数
RegisterRuntimeNativeMethods(self->GetJniEnv());
// 创建jit
CreateJit();
// 创建SystemClassLoader
system_class_loader_ = CreateSystemClassLoader(this);
...
...
return true;
}
// 执行Runtime相关的类的JNI注册
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
...
register_dalvik_system_VMRuntime(env);
register_java_lang_String(env); // 下面以String为例,展开具体函数逻辑
...
}
// string的native方法名,方法签名等信息
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(String, charAt, "(I)C"),
FAST_NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"),
FAST_NATIVE_METHOD(String, concat, "(Ljava/lang/String;)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, doRepeat, "(I)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, doReplace, "(CC)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, fastSubstring, "(II)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, getCharsNoCheck, "(II[CI)V"),
FAST_NATIVE_METHOD(String, fillBytesLatin1, "([BI)V"),
FAST_NATIVE_METHOD(String, fillBytesUTF16, "([BI)V"),
FAST_NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, toCharArray, "()[C"),
};
void register_java_lang_String(JNIEnv* env) {
REGISTER_NATIVE_METHODS("java/lang/String");
//该宏定义 最终调用了env->RegisterNatives(c.get(), methods, method_count);完成了动态注册
}
/************************创建 Runtime::strat end *********************************/
虚拟机创建成功之后,就为Java代码的运行提供了基础环境,为从cpp进入Java世界做好了准备
跟了上面的代码,我们了解到创建虚拟机的逻辑中就已经包含了一些JNI方法的动态注册了,不过主要是是虚拟机相关的。
动态注册系统JNI方法
紧接着第二步,注册Android系统原生的方法startReg(env)
c
// REG_JNI宏定义
#define REG_JNI(name) { name }
// 定义一个RegJNIRec,里面是一个函数类型的指针,函数的参数是JNIEnv指针
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
int AndroidRuntime::startReg(JNIEnv* env)
{
...
...
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
return 0;
}
// RegJNIRec数组,里面代表了需要注册的Java native方法
static const RegJNIRec gRegJNI[] = {
// register_com_android_internal_os_RuntimeInit 本身就是一个函数
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
...
...
};
// 注册函数
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
// 遍历gRegJNI数组的元素,并调用元素的方法mProc
// 比如array[0].mProc(env) 就是调用 register_com_android_internal_os_RuntimeInit(env)
if (array[i].mProc(env) < 0) {
return -1;
}
}
return 0;
}
// 动态注册RuntimeInit本地方法
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
// 可以看到主要是为两个native方法nativeFinishInit,nativeSetExitWithoutCleanup进行注册
// 他们映射的cpp函数分别是 com_android_internal_os_RuntimeInit_nativeFinishInit
// com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup
const JNINativeMethod methods[] = {
{"nativeFinishInit", "()V",
(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
{"nativeSetExitWithoutCleanup", "(Z)V",
(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
};
// jniRegisterNativeMethods最终调用JNI动态注册函数 env->RegisterNatives(clazz, methods, numMethods);
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
}
/**************libnativehelper/include/nativehelper/JNIHelp.h************************/
[[maybe_unused]] static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* methods,
int numMethods) {
using namespace android::jnihelp;
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
__android_log_assert("clazz == NULL", "JNIHelp",
"Native registration unable to find class '%s'; aborting...",
className);
}
// JNI的接口注册Java函数
int result = env->RegisterNatives(clazz, methods, numMethods);
env->DeleteLocalRef(clazz);
if (result == 0) {
return 0;
}
...
...
return result;
}
在虚拟机创建并运行之后,Android注册了系统默认的Java native方法,此时Java的代码还未运行,这是为进入Java世界之后native方法被调用做准备。
进入Java层
在JNI函数表成功加载,虚拟机创建完毕,JNI函数注册完成之后,进入Java的逻辑其实特别简单,就是通过类的全路径名找到对应的类,然后callStaticVoidMethod,就从cpp进入了ZygoteInit.java的main方法中了。
虚拟机如何建立Java到cpp的映射
至此,我们可以说cpp已经建立起了和Java的联系了,后续Java可以通过定义的native方法调用到cpp中映射的方法了。但是我们难免还有些疑惑或者好奇,就是是在哪里建立起了这种映射关系?答案就是JNI的注册接口RegisterNatives,其实这是JNI规范的重要接口,理论上它的实现因虚拟机而异,开发者不需要关系,我们只需要知道当我们调用了注册接口之后,cpp的函数就和Java对应的方法建立起了映射关系,调用Java native方法就会准确的调用到cpp的对应函数。
但是毕竟来都来了,再看看ART中关于JNI的部分实现也不是太复杂,不需要什么铺垫。
scss
/************************jni.h *********************************************/
// 这是声明在jni头文件里的注册函数
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
/*************************** jni_internal.cc ***********************************/
// 我们找到注册函数对应的定义实现
static jint RegisterNatives(JNIEnv* env,
jclass java_class,
const JNINativeMethod* methods,
jint method_count) {
...
...
// class_linker 是art中用于加载 链接,解析Java字节码的类
// 我们常说类的加载过程有三步:加载,链接,初始化,ClassLinker就是负责链接的部分
// classlinker的主要工作是验证、准备和解析
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
...
...
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
const void* fnPtr = methods[i].fnPtr;
if (UNLIKELY(name == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
return JNI_ERR;
} else if (UNLIKELY(sig == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
return JNI_ERR;
} else if (UNLIKELY(fnPtr == nullptr)) {
ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
return JNI_ERR;
}
// ArtMethod是Java 方法在虚拟机中运行时的表示
ArtMethod* m = nullptr;
bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
for (ObjPtr<mirror::Class> current_class = c.Get();
current_class != nullptr;
current_class = current_class->GetSuperClass()) {
// 通过class methodName signature三个参数来定位Java的方法
// <true>表示只在标识为native的方法中搜索
m = FindMethod<true>(current_class, name, sig);
// 找到就退出循环
if (m != nullptr) {
break;
}
// false 表示在非native方法中寻找
m = FindMethod<false>(current_class, name, sig);
if (m != nullptr) {
break;
}
}
// 如果m==null表示没找到Java层中定义的本地方法,返回错误
if (m == nullptr) {
...
...
return JNI_ERR;
} else if (!m->IsNative()) { // 找到了但是不是native标识的,也返回错误
...
...
return JNI_ERR;
}
//走到这里来,说明找到了Java层对应的native方法了
...
...
//调用class_linker.RegisterNative 把Java对应的方法m和cpp定义的函数fnPtr建立对应关系
const void* final_function_ptr = class_linker->RegisterNative(soa.Self(), m, fnPtr);
UNUSED(final_function_ptr);
}
return JNI_OK;
}
/**************************classlinker.cc **************************/
const void* ClassLinker::RegisterNative(
Thread* self, ArtMethod* method, const void* native_method) {
...
...
if (method->IsCriticalNative()) {
MutexLock lock(self, critical_native_code_with_clinit_check_lock_);
// Remove old registered method if any.
auto it = critical_native_code_with_clinit_check_.find(method);
if (it != critical_native_code_with_clinit_check_.end()) {
critical_native_code_with_clinit_check_.erase(it);
}
// To ensure correct memory visibility, we need the class to be visibly
// initialized before we can set the JNI entrypoint.
if (method->GetDeclaringClass()->IsVisiblyInitialized()) {
method->SetEntryPointFromJni(new_native_method);
} else {
critical_native_code_with_clinit_check_.emplace(method, new_native_method);
}
} else {
method->SetEntryPointFromJni(new_native_method); // 设置native函数调用点
}
return new_native_method;
}
/****************************** art_method.h *************************************/
// 这是ArtMethod内部的一个属性
// Must be the last fields in the method.
struct PtrSizedFields {
// Depending on the method type, the data is
// - native method: pointer to the JNI function registered to this method
// or a function to resolve the JNI function,
// - resolution method: pointer to a function to resolve the method and
// the JNI function for @CriticalNative.
// - conflict method: ImtConflictTable,
// - abstract/interface method: the single-implementation if any,
// - proxy method: the original interface method or constructor,
// - default conflict method: null
// - other methods: during AOT the code item offset, at runtime a pointer
// to the code item.
void* data_; //假如是native方法,data_会指向映射的cpp层定义的函数指针
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
// the interpreter.
// Java方法的入口
void* entry_point_from_quick_compiled_code_; //
} ptr_sized_fields_;
根据上面的分析我们可知,Java虚拟机加载了字节码之后,就可以在运行时轻易的找到对应的类和方法,然后让它和cpp的函数建立映射关系即可。
Java正常如何调用到cpp
由于Android的application是以Java为主要语言的,所以在cpp层进入Java层之后,逻辑的驱动就以Java为主了。
当我们正常启动一个app时。可能会在某个时刻需要调用native方法,但是我们知道其实app启动完成之前,cpp层已经完全保存好了Java层与cpp层的系统层面必要的方法映射关系了。但是开发者也会有一些自定义的native方法需要建立映射,而这个过程只能是在app启动之后进行了。
由于cpp层在开机时以及应用进程创建时就做好了一切准备工作,所以从Java层到cpp层的调用过程就没有那么复杂了:
- 提前加载我们打包好的so库
- 调用自定义的native方法
加载库的逻辑
从第一步起,代码如下
java
public class Some{
static{
// 加载我们打包的lib.so库
System.loadLibrary("lib")
}
}
/******************* NativeFun.java *****************************/
public class NativeFun {
native String callFromJavaAdd(int x,int y);
native String callFromJavaStr(String content,int v);
public void callFromCpp(String content){
}
}
而loadLibrary方法经过如下调用
scss
System.loadLibrary(String libname)
---Runtime.loadLibrary0(libName,classLoader);
------Runtime.nativeLoad(name,loader,ldLibraryPath);
// 最终调用到一个native方法中
Runtime{
...
...
// 这是一个系统定义的native 方法,我们直到开机时系统已经把这个方法在JNI中注册过了
private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
}
这个也是系统提供的JNI方法,实现在核心库libcore.so中,而且在我们使用时已经注册好了,
scss
/*****************************Runtime.c*****************************************/
//Runtime.nativeLoad方法所映射的cpp函数 Runtime_nativeLoad
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jclass caller)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
}
// 注册逻辑
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
NATIVE_METHOD(Runtime, nativeGc, "()V"),
NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
// NATIVE_METHOD是一个宏定义,最终结果是
//{"nativeLoad","(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)",Runtime_native}
// 一个JNINativeMethod类型的元素
NATIVE_METHOD(Runtime, nativeLoad,
"(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"
"Ljava/lang/String;"),
};
// 注册在libcore.so的JNI_OnLoad方法中
void register_java_lang_Runtime(JNIEnv* env) {
jniRegisterNativeMethods(env, "java/lang/Runtime", gMethods, NELEM(gMethods));
}
/****************************************** OpenjdkJvm.cc ************************************/
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jclass caller) {
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == nullptr) {
return nullptr;
}
std::string error_msg;
{
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
// 加载本地库
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
caller,
&error_msg);
if (success) {
return nullptr;
}
}
...
return env->NewStringUTF(error_msg.c_str());
}
本地库的加载最终调用了虚拟机的LoadNativeLibrary函数。
ini
/************************* art/runtime/jni/java_vm_ext.cc ****************/
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jclass caller_class,
std::string* error_msg) {
...
...
const char* path_str = path.empty() ? nullptr : path.c_str();
bool needs_native_bridge = false;
char* nativeloader_error_msg = nullptr;
//最终调用dlopen系统调用,打开给定的库
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);
...
...
bool was_successful = false;
// 最终调用dlsym系统调用,找到该so中的JNI_OnLoad方法
void* sym = library->FindSymbol("JNI_OnLoad", nullptr, android::kJNICallTypeRegular);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in "" << path << ""]";
was_successful = true;
} else {
...
...
using JNI_OnLoadFn = int(*)(JavaVM*, void*);
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
// 调用JNI_OnLoad()函数
int version = (*jni_on_load)(this, nullptr);
...
...
}
library->SetResult(was_successful);
return was_successful;
}
这里有必要解释下dlopen和dlsym这个系统调用:
- dlopen: 以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程
- dlsym: 通过dlopen返回的句柄和连接符名称获取函数名或者变量名 此外还有其他对应的系统调用:
- dlerror:返回出现的错误
- dlclose: 用来卸载打开的库
OpenNativeLibrary通过调用dlopen打开我们传入的so库。紧接着我们通过FindSymbol间接调用了dlsym,找到so中的JNI_OnLoad函数。而我们的动态注册一般就放在这个函数中。
不过我们还有静态注册的方式来建立映射关系,在so的加载阶段并没有做什么处理,正如之前文章所说,静态注册依赖JNI规定的固定格式的函数名来定位函数,因此只需要在Java层调用native函数时,动态查找对应的函数即可。
Java调用native函数
我们还是从开始的demo中说起,loadlibrary之后,我们可以正常的在合适的地方调用我们的native函数。当我们调用了native函数之后会发生什么呢?
在Java调用函数之前,虚拟机加载class时,会链接它,从中解析出对应的native函数(也是ArtMethod对象),然后为这个native函数设置好JNI调用入口:
scss
/******************************* class_linker.cc ************************************/
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()) {
// Set up the dlsym lookup stub. Do not go through `UnregisterNative()`
// as the extra processing for @CriticalNative is not needed yet.
// 我们自定义的JNI函数一般IsCriticalNative()是false
// GetJniDlsymLookupStub()内联到了 art_jni_dlsym_lookup_stub
method->SetEntryPointFromJni(
method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub());
}
}
/*************************** runtime_asm_entrypoints.h ***************************/
// art_jni_dlsym_lookup_stub指令链接在汇编代码中,
extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject);
static inline const void* GetJniDlsymLookupStub() {
return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
}
/******************************* jni_entrypoints_arm64.S*************************************/
// 汇编代码调用artFindNativeMethod
/*
* Jni dlsym lookup stub.
*/
.extern artFindNativeMethod
.extern artFindNativeMethodRunnable
ENTRY art_jni_dlsym_lookup_stub
// spill regs.
SAVE_ALL_ARGS_INCREASE_FRAME 2 * 8
stp x29, x30, [sp, ALL_ARGS_SIZE]
.cfi_rel_offset x29, ALL_ARGS_SIZE
.cfi_rel_offset x30, ALL_ARGS_SIZE + 8
add x29, sp, ALL_ARGS_SIZE
mov x0, xSELF // pass Thread::Current()
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
ldr xIP0, [x0, #THREAD_TOP_QUICK_FRAME_OFFSET] // uintptr_t tagged_quick_frame
bic xIP0, xIP0, #TAGGED_JNI_SP_MASK // ArtMethod** sp
ldr xIP0, [xIP0] // ArtMethod* method
ldr xIP0, [xIP0, #ART_METHOD_ACCESS_FLAGS_OFFSET] // uint32_t access_flags
mov xIP1, #(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE)
tst xIP0, xIP1
b.ne .Llookup_stub_fast_or_critical_native
bl artFindNativeMethod
b .Llookup_stub_continue
.Llookup_stub_fast_or_critical_native:
bl artFindNativeMethodRunnable
类加载阶段对方法进行了解析,给native方法指定了一个调用函数art_jni_dlsym_lookup_stub,而这个函数会链接到一段汇编代码 ,再从汇编代码跳转到artFindNativeMethod方法,去寻找它对应的cpp函数
scss
/*****************************jni_entrypoints.cc************************/
extern "C" const void* artFindNativeMethod(Thread* self) {
DCHECK_EQ(self, Thread::Current());
Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native.
ScopedObjectAccess soa(self);
return artFindNativeMethodRunnable(self);
}
extern "C" const void* artFindNativeMethodRunnable(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
Locks::mutator_lock_->AssertSharedHeld(self); // We come here as Runnable.
uint32_t dex_pc;
ArtMethod* method = self->GetCurrentMethod(&dex_pc); // 找到当前正在调用的方法
DCHECK(method != nullptr);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// Check whether we already have a registered native code.
// 先检查method中是否有我们动态注册的JNI函数,如果是静态注册,将找不到
const void* native_code = class_linker->GetRegisteredNative(self, method);
if (native_code != nullptr) {
return native_code;
}
// Lookup symbol address for method, on failure we'll return null with an exception set,
// otherwise we return the address of the method we found.
JavaVMExt* vm = down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm();
std::string error_msg;
// 尝试查找静态注册的函数
native_code = vm->FindCodeForNativeMethod(method, &error_msg, /*can_suspend=*/ true);
if (native_code == nullptr) {
LOG(ERROR) << error_msg;
self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", error_msg.c_str());
return nullptr;
}
// 注册当前JNI函数,并返回函数指针
return class_linker->RegisterNative(self, method, native_code);
}
/********************* java_vm_ext.cc *********************************/
void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m, std::string* error_msg, bool can_suspend) {
CHECK(m->IsNative());
ObjPtr<mirror::Class> c = m->GetDeclaringClass();
// If this is a static method, it could be called before the class has been initialized.
CHECK(c->IsInitializing() || !m->NeedsClinitCheckBeforeCall())
<< c->GetStatus() << " " << m->PrettyMethod();
Thread* const self = Thread::Current();
// 从本地已加载库的库中去查找
void* native_method = libraries_->FindNativeMethod(self, m, error_msg, can_suspend);
if (native_method == nullptr && can_suspend) {
// Lookup JNI native methods from native TI Agent libraries. See runtime/ti/agent.h for more
// information. Agent libraries are searched for native methods after all jni libraries.
native_method = FindCodeForNativeMethodInAgents(m);
}
return native_method;
}
void* FindNativeMethod(Thread* self, ArtMethod* m, std::string* detail, bool can_suspend)
REQUIRES(!Locks::jni_libraries_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
// JniShortName是指静态注册是不包含参数类型的函数名
std::string jni_short_name(m->JniShortName());
// // JniLongName是指静态注册包含参数类型的函数名
std::string jni_long_name(m->JniLongName());
...
...
void* native_code = nullptr;
android::JNICallType jni_call_type =
m->IsCriticalNative() ? android::kJNICallTypeCriticalNative : android::kJNICallTypeRegular;
if (can_suspend) { // can_suspend传入参数为true
ScopedThreadSuspension sts(self, ThreadState::kNative);
//查找对应的JNI方法
native_code = FindNativeMethodInternal(self,
declaring_class_loader_allocator,
shorty,
jni_short_name,
jni_long_name,
jni_call_type);
}
...
...
// 返回JNI函数
if (native_code != nullptr) {
return native_code;
}
...
...
}
//具体查找方式
void* FindNativeMethodInternal(Thread* self,
void* declaring_class_loader_allocator,
const char* shorty,
const std::string& jni_short_name,
const std::string& jni_long_name,
android::JNICallType jni_call_type)
REQUIRES(!Locks::jni_libraries_lock_) {
// 遍历当前缓存的library
for (const auto& lib : libraries_) {
SharedLibrary* const library = lib.second;
const char* arg_shorty = library->NeedsNativeBridge() ? shorty : nullptr;
//尝试用short_name从当前遍历的库中去查找函数
void* fn = library->FindSymbol(jni_short_name, arg_shorty, jni_call_type);
if (fn == nullptr) {
//如果找不到的话,再尝试用long_name从当前遍历的库中去查找函数
fn = library->FindSymbol(jni_long_name, arg_shorty, jni_call_type);
}
if (fn != nullptr) {
//最终返回对应的函数
return fn;
}
}
return nullptr;
}
// 以下是构建JNI的静态函数的方式
/**************************** art_method.cc***************************************/
std::string ArtMethod::JniShortName() {
return GetJniShortName(GetDeclaringClassDescriptor(), GetName());
}
std::string ArtMethod::JniLongName() {
std::string long_name;
long_name += JniShortName();
long_name += "__";
std::string signature(GetSignature().ToString());
signature.erase(0, 1);
signature.erase(signature.begin() + signature.find(')'), signature.end());
long_name += MangleForJni(signature);
return long_name;
}
/*******************************descriptors_names.cc******************************************/
std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
// Remove the leading 'L' and trailing ';'...
std::string class_name(class_descriptor);
CHECK_EQ(class_name[0], 'L') << class_name;
CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
class_name.erase(0, 1);
class_name.erase(class_name.size() - 1, 1);
std::string short_name;
short_name += "Java_";
short_name += MangleForJni(class_name);
short_name += "_";
short_name += MangleForJni(method);
return short_name;
}
至此关于Java调用JNI函数的全过程基本完成了,简单总结一下,就是在Java类加载阶段,虚拟机就会解析类的结构,同时为native函数指定调用的函数,当方法被调用时,会跳转到指定的函数,然后再对应的函数里找是否有已注册的已注册的JNI函数,假如没找到就用固定命名方式利用dlsym系统调用来查找函数。
cpp正常如何调用到Java
当我们理解了从Java如何调用到cpp的过程后,从cpp调用Java的过程就显得不是很复杂了。因为本身Java就运行在虚拟机的cpp代码之上,从cpp层是很容易找到运行时的Java方法的。
ini
// 假设我们定义了一个从cpp调用Java的方法,
extern "C"
JNIEXPORT void JNICALL call_java_method(JNIEnv *env,jobject obj) {
const char *className = "com/example/applicationnative/NativeFun";
jclass clazz = env->FindClass(className);
if (clazz == nullptr){
return;
}
jmethodID method_id = env->GetMethodID(clazz,"callFromCpp","(Ljava/lang/String;)V");
jstring content = env->NewStringUTF("来自cpp的问候");
env->CallVoidMethod(obj,method_id,content);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(content);
}
你会发现,其实我们大概需要关注三个函数的实现即可,分别是:
- FindClass 查找类
- GetMethodID 查找方法
- CallVoidMethod 调用方法
findClass
cpp
ObjPtr<mirror::Class> ClassLinker::FindClass(Thread* self,
const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
...
...
// 从已经加载的类中寻找对应的类
ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// 如果没找到,类又不是数组类I型那个,class_loader是null,就从boot class loader中去寻找
if (descriptor[0] != '[' && class_loader == nullptr) {
// Non-array class and the boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self,
descriptor,
hash,
ScopedNullHandle<mirror::ClassLoader>(),
*pair.first,
*pair.second);
}
...
}
ObjPtr<mirror::Class> result_ptr;
bool descriptor_equals;
if (descriptor[0] == '[') { //如果是数组类型
result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
...
..
} else {
// 从BaseDexClassLoader中寻找
bool known_hierarchy =
FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result_ptr);
...
...
}
...
...
return result_ptr;
}
ObjPtr<mirror::Class> ClassLinker::LookupClass(Thread* self,
const char* descriptor,
size_t hash,
ObjPtr<mirror::ClassLoader> class_loader) {
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
// 获取class_loader对应的class_table
ClassTable* const class_table = ClassTableForClassLoader(class_loader);
if (class_table != nullptr) {
// 从table中查找对应的类
ObjPtr<mirror::Class> result = class_table->Lookup(descriptor, hash);
if (result != nullptr) {
return result;
}
}
return nullptr;
}
GetMethodID
rust
static jmethodID GetMethodID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
...
...
return FindMethodID<kEnableIndexIds>(soa, java_class, name, sig, false);
}
// 模板函数
template<bool kEnableIndexIds>
static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
const char* name, const char* sig, bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_) {
// 调用FindMethodJNI函数
return jni::EncodeArtMethod<kEnableIndexIds>(FindMethodJNI(soa, jni_class, name, sig, is_static));
}
ArtMethod* FindMethodJNI(const ScopedObjectAccess& soa,
jclass jni_class,
const char* name,
const char* sig,
bool is_static) {
// 获取初始化之后的类对象
ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class));
if (c == nullptr) {
return nullptr;
}
ArtMethod* method = nullptr;
auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
if (c->IsInterface()) {
method = c->FindInterfaceMethod(name, sig, pointer_size);
} else {
// 我们调用的类一般不是接口,调用FindClassMethod
method = c->FindClassMethod(name, sig, pointer_size);
}
...
...
return method;
}
// FindClassMethod最终调用到这个模板函数
template <typename SignatureType>
static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass,
std::string_view name,
const SignatureType& signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Search declared methods first.
//从类中逐个扫描
for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) {
ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size);
// 对比函数名,函数签名,两者一致就返回
if (np_method->GetNameView() == name && np_method->GetSignature() == signature) {
return &method;
}
}
...
...
return uninherited_method; // Return the `uninherited_method` if any.
}
CallVoidMethod
当可以找到Java运行时的方法所对应的ArtMethod对象的ID之后,就是调用方法了。
scss
/************************** art/runtime/reflection.cc ***************************/
template <>
JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj,
jmethodID mid,
va_list args) {
// 把methodid解码为ArtMethod指针 调用InvokeVirtualOrInterfaceWithVarArgs
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, jni::DecodeArtMethod(mid), args);
}
template <>
JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj,
ArtMethod* interface_method,
va_list args) {
...
...
uint32_t shorty_len = 0;
const char* shorty =
method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
if (is_string_init) {
// For string init, remap original receiver to StringFactory result.
UpdateReference(soa.Self(), obj, result.GetL());
}
return result;
}
void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
ArtMethod* method, ArgArray* arg_array, JValue* result,
const char* shorty)
REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t* args = arg_array->GetArray();
// 调用Java方法
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
}
总结
关于JNI的实现,我们大概按照三个阶段来分析的,启动阶段的JNI构建阶段,正常运行时Java_call_JNI的阶段,以及在jni_call_java的阶段
构建阶段,在系统启动过程中,系统分加载jni相关的核心库,并导入相关的函数实现,同时创建虚拟机对象,并启动虚拟机,注册与此相关的JNI函数。
运行时java_call_jni app进程初始化时,系统会加载类时会对native方法进行解析,把native方法都指向一个固定的函数,然后链接到一段汇编代码中 Java层调用native方法前则需要先加载so库,系统会调用JNI_OnLoad函数,一般我们会在这个回调中添加注册JNI函数的方法,因此JNI_OnLoad也可能会触发注册函数相关的逻辑。在调用native方法后,我们会根据class链接时指向的调用路径,最终来到一段固定的汇编代码,通过artFindNativeMethod找到并执行注册的JNI函数。
JNI_call_java 从JNI call Java时,一般一切环境都已经准备好了,虚拟机通过classname找到jclass,再利用函数信息找到运行时Java方法所对应的ArtMethod的id,接着通过这些信息调用函数即可。