原标题:一文深入理解 JNI实现机制
写在前面
说到JNI都不陌生,它的全称:Java Native Interface,即Java本地接口。
JNI不仅仅是Android特有的,它是属于Java平台的,它允许在Java虚拟机内运行的java代码与其他编程语言(如c, c++和汇编语言)编写的程序和库进行交互。
JNI调用姿势:Java —> JNI —> C/C++(SO库)
在Android平台中,使用JNI封装了跟硬件相关的操作,从而可以通过Java调用相关JNI模块,以达到对硬件的调用。
下面我们将围绕如下内容进行展开:
1.java代码编译和执行过程
2.jni常规使用姿势
3.so加载流程(jni执行流程、注册方式原理)
4.JNIEnv作用及实现
Java代码编译和执行过程
java代码编译和执行的过程涉及到两个命令:javac和java
1.首先通过javac编译java代码,生成class字节码文件
2.javacTestCode.java --> TestCode.class
3.然后通过java命令来执行class字节码
4.javaTestCode.class
java命令执行过程中,会先启动虚拟机,加载TestCode类信息到内存,然后由执行引擎执行其main方法。
JVM的逻辑内存模型如下:
JNI常规使用姿势
在java层通过native关键字创建本地方法,如下:(成员方法、static方法)
public
classJNIMethods{
static{
System.loadLibrary( "test-jni");
}
//隐式注册
publicnativeString stringFromJNI();
publicstaticnativeString testParams(intjint, String jstring, Object jobject, Object[] arrayObject, Listlist);
//显式注册
publicnativeString stringFromJNIDis();
publicstaticnativeString testParamsDis(intjint, String jstring, Object jobject, Object[] arrayObject, Listlist);
}
创建c层代码,并注册本地方法
#include
#include
#include"common.h"
jstring stringFromJNIDis(JNIEnv *env, jobject jobj);
jstring testParamsDis(JNIEnv *env, jclass type, jint jint_, jstring jstring_, jobject jobject_, jobjectArray objectArray, jobject list);
staticjmethodID methodID_toString;
//显式注册
staticJNINativeMethod gMethods[] = {
{ "stringFromJNIDis", "()Ljava/lang/String;", ( void*) stringFromJNIDis},
{ "testParamsDis", "(ILjava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;Ljava/util/List;)Ljava/lang/String;", ( void*) testParamsDis}
};
staticintregisterNativeMethods(JNIEnv *env, constchar*className, JNINativeMethod *methods, intnMethods){
jclass clazz;
clazz = env->FindClass(className);
if(clazz == NULL) {
returnJNI_FALSE;
}
if(env->RegisterNatives(clazz, methods, nMethods) < 0) {
returnJNI_FALSE;
}
returnJNI_TRUE;
}
voidappendString(JNIEnv *env, char*dest, jstring tmpStr){
if(tmpStr == NULL) {
return;
}
constchar*tmpString = env->GetStringUTFChars(tmpStr, 0);
strcat(dest, tmpString);
env->ReleaseStringUTFChars(tmpStr, tmpString);
}
//加载so时会执行该方法
JNIEXPORT jint JNICALL JNI_(JavaVM *vm, void*reserved){
LOGD( "test-jni.cpp JNI_");
JNIEnv *env = NULL;
if(vm->GetEnv(( void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return-1;
}
if( NULL== env) {
return-1;
}
jclass clazz = env->FindClass( "java/lang/Object");
methodID_toString = env->GetMethodID(clazz, "toString", "()Ljava/lang/String;");
//显式注册本地方法
if(!registerNativeMethods(env, "com/mob/test/jni/testjni/JNIMethods", gMethods, NELEM(gMethods))) {
LOGE( "test-jni.cpp JNI_ register JNIMethods failed");
}
returnJNI_VERSION_1_4;
}
//卸载时会执行该方法
JNIEXPORT voidJNI_OnUnload(JavaVM *vm, void*reserved){
LOGD( "test-jni.cpp JNI_OnUnload");
methodID_toString = NULL;
}
//隐式注册
extern"C"JNIEXPORT jstring JNICALL
Java_com_mob_test_jni_testjni_JNIMethods_stringFromJNI(JNIEnv *env, jobject jobj){
LOGD( "test-jni.cpp Java_com_mob_test_jni_testjni_JNIMethods_stringFromJNI");
returnstringFromJNIDis(env, jobj);
}
extern"C"JNIEXPORT jstring JNICALL
Java_com_mob_test_jni_testjni_JNIMethods_testParams(JNIEnv *env, jclass type, jint jint_, jstring jstring_, jobject jobject_,
jobjectArray arrayObject,
jobject list){
LOGD( "test-jni.cpp Java_com_mob_test_jni_testjni_JNIMethods_testParams");
returntestParamsDis(env, type, jint_, jstring_, jobject_, arrayObject, list);
}
//显式注册
jstring stringFromJNIDis(JNIEnv *env, jobject jobj){
LOGD( "test-jni.cpp stringFromJNIDis");
std:: stringhello = "Hello from C++";
returnenv->NewStringUTF(hello.c_str());
}
jstring testParamsDis(JNIEnv *env, jclass type, jint jint_, jstring jstring_, jobject jobject_, jobjectArray arrayObject, jobject list){
LOGD( "test-jni.cpp testParamsDis");
charreturnValue[ 1024];
inti = jint_;
sprintf(returnValue, "%d", i);
appendString(env, returnValue, jstring_);
jstring returnToString = reinterpret_cast<_jstring>(env->CallObjectMethod(jobject_, methodID_toString));
appendString(env, returnValue, returnToString);
jsize len = env->GetArrayLength(arrayObject);
jobject tmpObject;
jstring tmpString;
for(i = 0; i < len; i++) {
tmpObject = env->GetObjectArrayElement(arrayObject, i);
tmpString = reinterpret_cast<_jstring>(env->CallObjectMethod(tmpObject, methodID_toString));
appendString(env, returnValue, tmpString);
}
env->DeleteLocalRef(tmpObject);
jclass clazz_list = env->GetObjectClass( list);
jmethodID list_get = env->GetMethodID(clazz_list, "get", "(I)Ljava/lang/Object;");
jmethodID list_size = env->GetMethodID(clazz_list, "size", "()I");
intsize = env->CallIntMethod( list, list_size);
jstring itemStr;
for(i = 0; i < size; i++) {
itemStr = reinterpret_cast(env->CallObjectMethod( list, list_get, i));
appendString(env, returnValue, itemStr);
}
returnenv->NewStringUTF(returnValue);
}
在gradle中配置cmake编译选项,并编写CMakeLists.txt脚本,用于生成soandroid {
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
#CMakeList.txt 内容如下:
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
add_library( # Sets the name of the library.
test-jni
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/ test-jni.cpp)
find_library( # Sets the name of the path variable.
test
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
target_link_libraries( # Specifies the target library.
test-jni
# Links the target library to the log library
# included in the NDK.
${log-lib})
在java层,加载本地方法前,加载so
public
classJNIMethods{
static{
//加载so
System.loadLibrary( "test-jni");
}
//隐式注册
publicnativeString stringFromJNI();
publicstaticnativeString testParams(intjint, String jstring, Object jobject, Object[] arrayObject, Listlist);
//显式注册
publicnativeString stringFromJNIDis();
publicstaticnativeString testParamsDis(intjint, String jstring, Object jobject, Object[] arrayObject, Listlist);
}
so加载流程
一般我们是通过 System.loadLibrary("test-jni") 的方式来加载libtest-jni.so库的,一起来跟踪下整个加载过程(此处分析的源码是基于Dalvik的)
System.loadLibrary() -> Runtime.loadLibray0() -> Runtime.doLoad() -> Runtime.nativeLoad()
//java.lang.System
publicstaticvoidloadLibrary(String libname){
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
//java.lang.Runtime
synchronizedvoidloadLibrary0(ClassLoader loader, String libname){
//......
String error = doLoad(filename, loader);
//......
}
privateString doLoad(String name, ClassLoader loader){
//......
synchronized( this) {
returnnativeLoad(name, loader, librarySearchPath);
}
}
// TODO:should be synchronized, but dalvik doesn't support synchronized internal natives.
privatestaticnativeString nativeLoad(String filename, ClassLoader loader, String librarySearchPath);
在代码中我们并没有看到相关加载nativeLoad这个本地方法的so,那么它又是用什么方式在什么时候被注册的呢?
很多系统的本地方法都是在应用启动的时候通过显示注册的方式被注册的,下面我们来看下系统启动后会做哪些事情。
启动虚拟机进程app_process首先会执行app_main.cpp中的main方法
/frameworks/base/cmds/app_process/app_main. cpp
main()-> AndroidRuntime.start()
intmain(intargc, char* constargv[])
{
//......
if(zygote) {
runtime.start( "com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server": "");
} elseif(className) {
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start( "com.android.internal.os.RuntimeInit",
application ? "application": "tool");
}
//......
}
/frameworks/base/core/jni/AndroidRuntime. cpp
start()->startVm()
voidAndroidRuntime::start(constchar* className, constchar* options)
{
//......
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if(startVm(&mJavaVM, &env) != 0) { //启动vm
return;
}
}
intAndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
//......
if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { //创建vm
ALOGE( "JNI_CreateJavaVM failedn");
goto bail;
}
//......
}
JNI_CreateJavaVM()在art和dalvik中都有相关实现,这里仅以dalvik为主线跟踪,art的实现在/art/runtime/jni_internal.cc中
/dalvik/vm/Jni. cpp
JNI_CreateJavaVM()-> Init.dvmStartUp()
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args){
//......
JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
/* Initialize VM. */
gDvm.initializing = true;
std::string status = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv); //初始化vm
//......
}
/dalvik/vm/Init. cpp
dvmStartUp()-> InternalNative.dvmInternalNativeStartup()
dvmStartUp()-> registerSystemNatives()-> jni.RegisterNatives()
std::string dvmStartup(intargc, constchar* constargv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
//......
if(!dvmNativeStartup()) { //初始化native
return"dvmNativeStartup failed";
}
if(!dvmInternalNativeStartup()) { //初始化内部native
return"dvmInternalNativeStartup failed";
}
if(!dvmJniStartup()) { //初始化jni
return"dvmJniStartup failed";
}
//......
if(!registerSystemNatives(pEnv)) { //注册系统本地方法
return"couldn't register system natives";
}
//......
return"";
}
staticbool registerSystemNatives(JNIEnv* pEnv)
{
//......
JNIEXPORT jobject JNICALL Java_java_lang_Class_getDex(JNIEnv* env, jclass javaClass);
constJNINativeMethod Java_java_lang_Class[] = {
{ "getDex", "()Lcom/android/dex/Dex;", ( void*) Java_java_lang_Class_getDex },
};
//注册Dex的getDex方法
if(pEnv->RegisterNatives(c, Java_java_lang_Class, 1) != JNI_OK) {
dvmAbort();
}
//加载javacore和nativehelper库
loadJniLibrary( "javacore");
loadJniLibrary( "nativehelper");
// Back to run mode.
self->status = THREAD_RUNNING;
returntrue;
}
/dalvik/vm/native/InternalNative.cpp
dvmInternalNativeStartup()
bool dvmInternalNativeStartup()
{
DalvikNativeClass* classPtr = gDvmNativeMethodSet; //遍历系统类结构体,并保存到gDvm的全局变量userDexFiles中
while(classPtr->classDeor != NULL) {
classPtr->classDeorHash =
dvmComputeUtf8Hash(classPtr->classDeor);
classPtr++;
}
gDvm.userDexFiles = dvmHashTableCreate( 2, dvmFreeDexOrJar);
if(gDvm.userDexFiles == NULL)
returnfalse;
returntrue;
}
//系统本地方法都存在这里
staticDalvikNativeClass gDvmNativeMethodSet[] = {
{ "Ljava/lang/Object;", dvm_java_lang_Object, 0},
{ "Ljava/lang/Class;", dvm_java_lang_Class, 0},
//……
{ "Ljava/lang/Runtime;", dvm_java_lang_Runtime, 0},
{ "Ljava/lang/String;", dvm_java_lang_String, 0},
{ "Ljava/lang/System;", dvm_java_lang_System, 0},
//……
{ "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0},
{ "Ldalvik/system/VMRuntime;", dvm_dalvik_system_VMRuntime, 0},
//……
};
//调用本地方法会到这里来寻找系统本地方
DalvikNativeFunc dvmLookupInternalNativeMethod( constMethod* method)
{
constchar* classDeor = method->clazz->deor;
constDalvikNativeClass* pClass;
u4 hash;
hash = dvmComputeUtf8Hash(classDeor);
pClass = gDvmNativeMethodSet; //遍历全局本地方法集合,寻找注册的系统本地方法
while( true) {
if(pClass->classDeor == NULL)
break;
if(pClass->classDeorHash == hash &&
strcmp(pClass->classDeor, classDeor) == 0)
{
constDalvikNativeMethod* pMeth = pClass->methodInfo;
while( true) {
if(pMeth->name == NULL)
break;
if(dvmCompareNameDeorAndMethod(pMeth->name,
pMeth->signature, method) == 0)
{
/* match */
returnpMeth->fnPtr;
}
pMeth++;
}
}
pClass++;
}
returnNULL;
}
注册本地方法
/dalvik/vm/Jni. cpp
RegisterNatives()-> dvmRegisterJNIMethod()-> dvmUseJniBridge() -> Class.dvmSetNativeFunc()
staticjint RegisterNatives(JNIEnv* env, jclass jclazz, constJNINativeMethod* methods, jint nMethods)
{
//......
for( inti = 0; i < nMethods; i++) {
if(!dvmRegisterJNIMethod(clazz, methods[i].name,
methods[i].signature, methods[i].fnPtr))
{
returnJNI_ERR;
}
}
returnJNI_OK;
}
staticbooldvmRegisterJNIMethod(ClassObject* clazz, constchar* methodName,
constchar* signature, void* fnPtr)
{
//......
if(method->nativeFunc != dvmResolveNativeMethod) {
/* this is allowed, but unusual */
ALOGV( "Note: %s.%s:%s was already registered", clazz->deor, methodName, signature);
}
method->fastJni = fastJni;
dvmUseJNIBridge(method, fnPtr);
ALOGV( "JNI-registered %s.%s:%s", clazz->deor, methodName, signature);
returntrue;
}
voiddvmUseJNIBridge(Method* method, void* func){
//……
DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;
dvmSetNativeFunc(method, bridge, ( constu2*) func); //将nativeFunc指向dvmCallJNIMethod,并将insns指向func
}
voiddvmCallJNIMethod(constu4* args, JValue* pResult, constMethod* method, Thread* self){
//......
JNIEnv* env = self->jniEnv;
COMPUTE_STACK_SUM(self);
dvmPlatformInvoke(env, (ClassObject*) staticMethodClass,
method->jniArgInfo, method->insSize, modArgs, method->shorty,
( void*) method->insns, pResult); //调用方法insns,对于不同的cpu架构会有不同的调用方式
//......
}
/dalvik/vm/oo/Class. cpp
dvmSetNativeFunc()
voiddvmSetNativeFunc(Method* method, DalvikBridgeFunc func, constu2* insns)
{
//......
//将nativeFunc指向dvmCallJNIMethod,并将insns指向func
if(insns != NULL) {
/* update both, ensuring that "insns" is observed first */
method->insns = insns;
android_atomic_release_store((int32_t) func,
( volatileint32_t*)( void*) &method->nativeFunc);
} else{
/* only update nativeFunc */
method->nativeFunc = func;
}
//......
}
至此,我们可以知道调用JNI方法最终会通过 Jni.dvmCallJNIMethod() -> dvmPlatformInvoke() 来根据不同cpu架构实现进行调用。
Class被加载的过程
Android中ClassLoader都是继承自BaseDexClassLoader,加载dex是通过BaseDexClassLoader来加载的,会将dex中的信息都保存到DexPathList中,后面加载类的过程如下:
BaseDexClassLoader.findClass() -> DexPathList.findClass() -> 遍历dexElements -> DexFile.loadClassBinaryName() -> DexFile.defineClass() -> DexFile.defineClassNative()
/dalvik/vm/ native/dalvik_system_DexFile. cpp
defineClassNative()-> Class.dvmDefineClass()
constDalvikNativeMethod dvm_dalvik_system_DexFile[] = {
//……
{ "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
Dalvik_dalvik_system_DexFile_defineClassNative },
{ "getClassNameList", "(I)[Ljava/lang/String;",
Dalvik_dalvik_system_DexFile_getClassNameList },
//……
};
staticvoidDalvik_dalvik_system_DexFile_defineClassNative(constu4* args,
JValue* pResult)
{
//......
clazz = dvmDefineClass(pDvmDex, deor, loader);
Thread* self = dvmThreadSelf();
//......
}
/dalvik/vm/oo/Class. cpp
dvmDefineClass()-> findClassNoInit()-> dvmLookupClass()
-> loadClassFromDex()-> loadClassFromDex0()-> loadMethodFromDex()-> dvmResolveNativeMethod()
ClassObject* dvmDefineClass(DvmDex* pDvmDex, constchar* deor,
Object* classLoader)
{
assert(pDvmDex != NULL);
returnfindClassNoInit(deor, classLoader, pDvmDex);
}
staticClassObject* findClassNoInit( constchar* deor, Object* loader,
DvmDex* pDvmDex)
{
Thread* self = dvmThreadSelf();
ClassObject* clazz;
//......
clazz = dvmLookupClass(deor, loader, true); //从gDvm全局变量的loadedClasses中找
if(clazz == NULL) {
clazz = loadClassFromDex(pDvmDex, pClassDef, loader); //没找到,则从dex中找
}
//......
returnclazz;
}
staticClassObject* loadClassFromDex(DvmDex* pDvmDex, constDexClassDef* pClassDef, Object* classLoader)
{
ClassObject* result;
//......
result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
classLoader);
//......
returnresult;
}
staticClassObject* loadClassFromDex0(DvmDex* pDvmDex,
constDexClassDef* pClassDef, constDexClassDataHeader* pHeader,
constu1* pEncodedData, Object* classLoader)
{
ClassObject* newClass = NULL;
//......
loadIFieldFromDex(newClass, &field, &newClass->ifields[i]); //遍历加载成员变量
//......
loadMethodFromDex(newClass, &method, &newClass->directMethods[i]); //遍历加载方法
//......
newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
returnnewClass;
}
staticvoidloadMethodFromDex(ClassObject* clazz, constDexMethod* pDexMethod, Method* meth)
{
//......
if(dvmIsNativeMethod(meth)) { //如果是本地方法,会将nativeFunc指向dvmResolveNativeMethod,当执行本地方法时就会执行dvmResolveNativeMethod方法
meth->nativeFunc = dvmResolveNativeMethod;
meth->jniArgInfo = computeJniArgInfo(&meth->prototype);
}
//......
}
上面跟踪下来我们知道每个dex中的本地方法都会执行到 dvmResolveNativeMethod(),我们来看看该方法做了哪些事情?
/dalvik/vm/Native.cpp
dvmResolveNativeMethod() -> InternalNative.dvmLookupInternalNativeMethod()
dvmResolveNativeMethod() -> lookupSharedLibMethod() -> findMethodInLib()
voiddvmResolveNativeMethod( constu4* args, JValue* pResult,
constMethod* method, Thread* self)
{
//……
/* start with our internal-native methods */
DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method); //寻找系统内部本地方法
if(infunc != NULL) {
//……
DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
dvmSetNativeFunc((Method*) method, dfunc, NULL);
dfunc(args, pResult, method, self); //执行
return;
}
/* now scan any DLLs we have loaded for JNI signatures */
void* func = lookupSharedLibMethod(method); //在动态加载的so中寻找本地方法
if(func != NULL) {
/* found it, point it at the JNI bridge and then call it */
dvmUseJNIBridge((Method*) method, func);
(*method->nativeFunc)(args, pResult, method, self); //执行nativeFunc
return;
}
//……
dvmThrowUnsatisfiedLinkError( "Native method not found", method); //没找到本地方法,则会报UnsatisfiedLinkError
}
staticvoid* lookupSharedLibMethod( constMethod* method)
{
if(gDvm.nativeLibs == NULL) {
ALOGE( "Unexpected init state: nativeLibs not ready");
dvmAbort();
}
return( void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
( void*) method); //先从gDvm全局变量nativeLibs中寻找,没找到,则通过findMethodInLib在so中寻找,找到则保存到nativeLibs中
}
staticintfindMethodInLib( void* vlib, void* vmethod)
{
constSharedLib* pLib = ( constSharedLib*) vlib;
constMethod* meth = ( constMethod*) vmethod;
//......
/*
* First, we try it without the signature.
*/
preMangleCM =
createJniNameString(meth->clazz->deor, meth->name, &len);
if(preMangleCM == NULL)
gotobail;
mangleCM = mangleString(preMangleCM, len);
if(mangleCM == NULL)
gotobail;
//通过createJniNameString和mangleString,得到最终函数的名称路径Java_package_methodName
ALOGV( "+++ calling dlsym(%s)", mangleCM);
func = dlsym(pLib->handle, mangleCM); //根据动态链接库操作句柄与符号,返回符号对应的地址
//......
return( int) func;
}
系统内部本地方法通过 dvmLookupInternalNativeMethod()方法来寻找具体实现的
通过System.loadLibrary加载的本地方法,则是通过 lookupSharedLibMethod()方法来寻找的
(方法名称格式:Java_package_methodName)
/dalvik/vm/native/InternalNative.cpp
dvmLookupInternalNativeMethod() -> java_lang_runtime.Dalvik_java_lang_Runtime_nativeLoad()
DalvikNativeFunc dvmLookupInternalNativeMethod( constMethod* method)
{
constchar* classDeor = method->clazz->deor;
constDalvikNativeClass* pClass;
u4 hash;
hash = dvmComputeUtf8Hash(classDeor);
pClass = gDvmNativeMethodSet; //遍历全局本地方法集合,寻找内部注册的本地方法
while( true) {
if(pClass->classDeor == NULL)
break;
if(pClass->classDeorHash == hash &&
strcmp(pClass->classDeor, classDeor) == 0)
{
constDalvikNativeMethod* pMeth = pClass->methodInfo;
while( true) {
if(pMeth->name == NULL)
break;
if(dvmCompareNameDeorAndMethod(pMeth->name,
pMeth->signature, method) == 0)
{
/* match */
//ALOGV("+++ match on %s.%s %s at %p",
// className, methodName, methodSignature, pMeth->fnPtr);
returnpMeth->fnPtr;
}
pMeth++;
}
}
pClass++;
}
returnNULL;
}
staticDalvikNativeClass gDvmNativeMethodSet[] = {
{ "Ljava/lang/Object;", dvm_java_lang_Object, 0},
{ "Ljava/lang/Class;", dvm_java_lang_Class, 0},
//……
{ "Ljava/lang/Runtime;", dvm_java_lang_Runtime, 0},
{ "Ljava/lang/String;", dvm_java_lang_String, 0},
{ "Ljava/lang/System;", dvm_java_lang_System, 0},
{ "Ljava/lang/Throwable;", dvm_java_lang_Throwable, 0},
{ "Ljava/lang/VMClassLoader;", dvm_java_lang_VMClassLoader, 0},
{ "Ljava/lang/VMThread;", dvm_java_lang_VMThread, 0},
//……
{ "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0},
{ "Ldalvik/system/VMRuntime;", dvm_dalvik_system_VMRuntime, 0},
//……
};
/dalvik/vm/native/java_lang_runtime.cpp
java_lang_runtime.Dalvik_java_lang_Runtime_nativeLoad() -> Native.dvmLoadNativeCode()
constDalvikNativeMethod dvm_java_lang_Runtime[] = {
//……
{ "nativeExit", "(I)V",
Dalvik_java_lang_Runtime_nativeExit },
{ "nativeLoad", "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;",
Dalvik_java_lang_Runtime_nativeLoad },
//……
};
staticvoid Dalvik_java_lang_Runtime_nativeLoad( constu4* args,
JValue* pResult)
{
//......
bool success = dvmLoadNativeCode(fileName, classLoader, &reason);
//......
}
/dalvik/vm/Native.cpp
dvmLoadNativeCode() -> JNI_()
bool dvmLoadNativeCode( constchar* pathName, Object* classLoader,
char** detail)
{
SharedLib* pEntry;
//......
pEntry = findSharedLibEntry(pathName); //so被加载过一次之后,就会缓存起来
if(pEntry != NULL) {
//......
returntrue;
}
//……
//so第一次被加载
handle = dlopen(pathName, RTLD_LAZY); //打开so动态链接库,保存句柄到handle
//......
/* create a new entry */
SharedLib* pNewEntry;
pNewEntry = (SharedLib*) calloc( 1, sizeof(SharedLib));
pNewEntry->pathName = strdup(pathName);
pNewEntry->handle = handle; //保存handle到pNewEntry
//……
SharedLib* pActualEntry = addSharedLibEntry(pNewEntry); //将pNewEntry保存到gDvm全局变量nativeLibs中,下次可以直接通过缓存获取
//......
v = dlsym(handle, "JNI_"); //第一次加载so时,调用so中的JNI_方法
//......
returnresult;
}
}
上面分析总结:
通过System.loadLibrary()加载的库会被保存到全局变量gDvm.nativeLibs中,并执行其JNI_方法。
JNI显式注册是直接将本地方法调用地址指向到c层某方法,并将执行方法改成dvmCallJNIMethod(),隐式是通过方法命名规则去寻找方法的调用地址,然后通过dvmCallJNIMethod()来执行类被加载时,所有的本地方法执行地址nativeFunc都被指向了Native.dvmResolveNativeMethod()
通过JNI显式注册会将本地方法调用地址执行C能某实现方法,并将执行地址nativeFunc指向dvmCallJNIMethod(),隐式注册则是在调用的时候才会通过命名规则去寻找方法的调用地址,寻找到之后,再将执行地址nativeFunc指向dvmCallJNIMethod()
所以执行native方法时,nativeFunc未被指向到dvmCallJNIMethod()的方法(包括第一次执行的隐式注册方法和未被注册的系统方法)都会通过
Native.dvmResolveNativeMethod()来处理,针对系统本地方法,会从 InternalNative.gDvmNativeMethodSet 中寻找;针对应用本地方法,则从 gDvm.nativeLibs 中寻找。
当第二次执行同一个方法时,则会直接执行dvmCallJNIMethod(),所以隐式注册相对显示注册来说,第一次调用的时候会慢一点,但是初始化快。
Jni.dvmCallJNIMethod()执行本地方法,是通过调用dvmPlatformInvoke() 来根据不同cpu架构实现方式来执行的。
so加载核心过程:
class中本地方法加载过程:
本地方法调用过程:
JNIEnv作用及实现
JNIEnv是JNI指针接口,通常我们通过它来调用操作Java对象,它提供了所有的操作java对象的api。
那么JNIEnv又是何时创建,以及如何来操作Java对象的呢?我们一起来看看它的具体实现~
首先我们来看下线程时如何被创建的:
java/lang/Thread. java
new Thread().start()-> VMThread.create()
publicsynchronizedvoidstart(){
checkNotStarted();
hasBeenStarted = true;
VMThread.create( this, stackSize); // VMThread创建
}
/dalvik/vm/ native/java_lang_VMThread. cpp
Dalvik_java_lang_VMThread_create()-> Thread.dvmCreateInterpThread()
constDalvikNativeMethod dvm_java_lang_VMThread[] = {
{ "create", "(Ljava/lang/Thread;J)V",
Dalvik_java_lang_VMThread_create }, //线程创建方法
{ "currentThread", "()Ljava/lang/Thread;",
Dalvik_java_lang_VMThread_currentThread },
//……
};
staticvoidDalvik_java_lang_VMThread_create(constu4* args, JValue* pResult)
{
//……
dvmCreateInterpThread(threadObj, ( int) stackSize); //创建线程
RETURN_VOID();
}
/dalvik/vm/Thread. cpp
dvmCreateInterpThread()-> interpThreadStart()-> jni.dvmCreateJNIEnv()
bool dvmCreateInterpThread(Object* threadObj, intreqStackSize)
{
//......
Thread* newThread = allocThread(stackSize);
//......
intcc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread); //创建线程
// ......
}
/*
* pthread entry function for threads started from interpreted code.
*/
staticvoid* interpThreadStart( void* arg)
{
//......
self->jniEnv = dvmCreateJNIEnv(self); //创建jniEnv
//......
returnNULL;
}
JNIEnv的创建
/dalvik/vm/jni.cpp
dvmCreateJNIEnv()
JNIEnv* dvmCreateJNIEnv(Thread* self) {
JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
//……
newEnv->funcTable = &gNativeInterface; //接口函数
//......
return(JNIEnv*) newEnv;
}
staticconststruct JNINativeInterface gNativeInterface = {
DefineClass,
FindClass,
//......
CallVoidMethod,
//...
GetFieldID,
GetObjectField,
//...
CallStaticIntMethod,
//......
};
//方法具体实现(下面是callVoidMethodA()具体实现)
#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref)
static_ctype Call ##_jname##Method(JNIEnv* env, jobject jobj,
jmethodID methodID, ...)
{
ScopedJniThreadState ts(env);
Object* obj = dvmDecodeIndirectRef(ts. self(), jobj);
constMethod* meth;
va_list args;
JValue result;
meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);
if(meth == NULL) {
return_retfail;
}
va_start(args, methodID);
dvmCallMethodV(ts. self(), meth, obj, true, &result, args); //方法调用
va_end(args);
if(_isref && !dvmCheckException(ts. self()))
result.l = (Object*)addLocalReference(ts. self(), result.l);
return_retok;
}
CALL_VIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
//......
/dalvik/vm/interp/Stack.cpp
JNIEnv.CallVoidMethodA() -> Stack.dvmCallMethodA()
void dvmCallMethodA(Thread* self, constMethod* method, Object* obj,
bool fromJni, JValue* pResult, constjvalue* args)
{
//......
if(dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER( self, method);
(*method->nativeFunc)((u4*) self->interpSave.curFrame, pResult,
method, self); //执行方法
TRACE_METHOD_EXIT( self, method);
}
//……
}
JNIEnv是在Thread创建的时候创建的,因此它是属于线程的,不能线程共享。最终调用方法时通过Stack.dvmCallMethod..相关方法去调用的。至此整个流程就分析完毕了。
结语
通过上面的分析,我们知道了JNI是如何在java和本地代码之间建立桥梁和通讯的。
在使用JNI的时候,需要注意:
1.显式注册和隐式注册的区别
2.JNI_方法的使用
3.JNIEnv的使用
4.JNI方法的命名,以及extern “C"的作用(告诉编译器,这部分代码请使用C来编译)等等
大家都在看
欢迎前往安卓巴士博客区投稿,技术成长于分享
期待巴友留言,共同探讨学习返回搜狐,查看更多
责任编辑: