Android JNIAndroid开发经验谈Android开发

ndk09_JNI源码分析与JNI动态注册解决javah生成jn

2017-10-24  本文已影响43人  IT魔幻师

一、前言

我们知道cpu只认得“0101101”类似这种符号,C、C++这些代码最终都得通过编译、汇编成二进制代码,cpu才能识别。而Java比C、C++又多了一层虚拟机,过程也复杂许多。Java代码经过编译成class文件、虚拟机装载等步骤最终在虚拟机中执行。class文件里面就是一个结构复杂的表,而最终告诉虚拟机怎么执行的就靠里面的字节码说明。

Java虚拟机在执行的时候,可以采用解释执行和编译执行的方式执行,但最终都是转化为机器码执行。

Java虚拟机运行时的数据区,包括方法区、虚拟机栈、堆、程序计数器、本地方法栈。

问题来了,按我们目前的理解,如果是解释执行,那么方法区中应该存的是字节码,那执行的时候,通过JNI动态装载的c、c++库,怎么加载进来的?

二、JNI源码执行流程

    1. javac 与javap 处理 native方法
            ACC_NATIVE
            可见,普通的“add”方法是直接把字节码放到code属性表中,而native方法,
            与普通的方法通过一个标志“ACC_NATIVE”区分开来。java在执行普通的方法调用的时候,
            可以通过找方法表,再找到相应的code属性表,最终解释执行代码,那么,对于native方法,
            在class文件中,并没有体现native代码在哪里,只有一个“ACC_NATIVE”的标识,
            那么在执行的时候改怎么找到动态链接库的代码呢?
        
    2. System.loadLibrary()
            ---->Runtime.loadLibrary0
            --->分两步:
                ----->1. 找到so库文件
                    两种方案:
                        1. ClassLoader
                                    
                            通过打印,知道实际的ClassLoder是PathClassLoader
                            ----->findLibrary
                                        
                            ---->this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
                                            
                            ---->DexPathList.java
                            ---->findLibrary
                            ---->分析构造函数
                            解释路径
                                    
                        2. ClassLoader为空
                            ----->getLibPaths()
                            ----->System.mapLibraryName //根据库类型 添加libxxx.so 还是xxx.dll
                                        
                ----->2. 加载so库文件
                    ----->Runtime.java 
                    ---->doLoad(filename, loader);
                    ----->//libcore/ojluni/src/main/native/Runtime.c
                    --->Runtime_nativeLoad
                    预留动态注册:
                        jniRegisterNativeMethods
                        JVM_NativeLoad
                        ---->//art/runtime/openjdkjvm/OpenjdkJvm.cc
                        ---->LoadNativeLibrary()
                        ---->sym = library->FindSymbol("JNI_OnLoad", nullptr);
                            //在我们要加载so库中查找JNI_OnLoad方法,如果没有系统就认为是静态注册方式进行的,
                            直接返回true,代表so库加载成功,如果找到JNI_OnLoad就会调用JNI_OnLoad方法,
                            JNI_OnLoad方法中一般存放的是方法注册的函数,所以如果采用动态注册就必须要实现JNI_OnLoad方法,
                            否则调用java中申明的native方法时会抛出异常,下面有JNI_OnLoad的实现
                        ----->JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
                        ----->IsBadJniVersion(version)
                        ------>version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;

三、JNI动态注册用法

四、多个java类里面的native方法动态注册

    static const JNINativeMethod gMethods[] = {
            {
                    "diff",  "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_diff
            },
            {
                    "patch", "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_patch
            }
    };
    
    static const JNINativeMethod gMethods_MainActivity[] = {
            {
                    "newJniThread", "()V", (void *) native_newThead
            },
            {
                    "setJniEnv",    "()V", (void *) native_setJniEnv
            }
    };
    
    
    //注册FileUtils 里面native方法
    static int registerNatives(JNIEnv *engv) {
        LOGI("registerNatives begin");
        jclass clazz;
        clazz = (*engv)->FindClass(engv, "com/hubin/demo/FileUtils");
    
        if (clazz == NULL) {
            LOGI("clazz is null");
            return JNI_FALSE;
        }
    
        if ((*engv)->RegisterNatives(engv, clazz, gMethods, NELEM(gMethods)) < 0) {
            LOGI("RegisterNatives error");
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    //注册mainactivity中的native方法
    static int registerNatives_Main(JNIEnv *engv) {
        LOGI("registerNatives_Main begin");
        jclass clazz;
        clazz = (*engv)->FindClass(engv, "com/hubin/demo/MainActivity");
    
        if (clazz == NULL) {
            LOGI("clazz is null");
            return JNI_FALSE;
        }
    
        if ((*engv)->RegisterNatives(engv, clazz, gMethods_MainActivity, NELEM(gMethods_MainActivity)) < 0) {
            LOGI("registerNatives_Main error");
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    
    
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env = NULL;
        jint result = -1;
        if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
            LOGI("ERROR: GetEnv failed\n");
            return -1;
        }
        assert(env != NULL);
    
        //为了方便管理我们将不同java类中的native方法分别注册
        registerNatives(env);   //注册第一个类中的native方法
        registerNatives_Main(env); //注册第二个类中的native方法
    
        return JNI_VERSION_1_4;
    }

五、静态注册和动态注册的区别

上一篇 下一篇

猜你喜欢

热点阅读