美文网首页
NDK开发之JNI基础(2),静态注册和动态注册

NDK开发之JNI基础(2),静态注册和动态注册

作者: tianyl | 来源:发表于2019-02-14 22:13 被阅读0次

    JNI的编译过程

    当使用JNI时,实现在xxx.c文件中的方法,会经过编译,链接之后打包到apk文件中,其中分为编译,链接两个步骤

    编译

    xxx.c文件生成xxx.obj文件(Windows平台)或xxx.o文件(linux平台)
    在编译过程,会做语法检查

    链接

    .o文件链接成xxx.so文件
    将多个.c文件和.h文件生成.so文件,并且检查多个文件的引用的方法是否存在(比如a文件引用了b文件的方法,但是b中没有这个方法,那么就会链接出错)

    编译规则

    eclipse中,使用的是GUN规则,写在Android.mk文件中
    在AndroidStudio中,使用的是LLVM规则,写在CMakeList.txt文件中
    它们都三段式编译器

    因为Android Studio中主要使用的是CMakeList,所以这里就略去Android.mk的语法说明,相关Android.mk的语法可以查看Google的官方文档或者使用搜索引擎

    因为CMakeList语法说明较长,所以单独说明

    添加现有项目支持JNI

    如果想在新项目中使用JNI,那么只需要勾上NDK工具的选项即可,但是如果想修改现有的项目支持JNI,那么就稍微麻烦一些了,具体步骤如下

    1. 首先需要配置NDK路径,下载所以编译需要的插件(LLDB、NDK和CMake)官方说明
    2. 在现有的main目录下新建cpp目录,和java目录同级


    3. 在app的build.gradle文件中添加说明
    //写在Android标签下
    android {
        ...
        defaultConfig {
            ...
            externalNativeBuild {
                cmake {
                    //这里是创建项目时勾选的支持
                    cppFlags "-frtti -fexceptions"
                }
            }
        }
        
        externalNativeBuild {
            cmake {
                //这里是CMakeLists文件的路径
                path "CMakeLists.txt"
            }
        }
    }
    
    • -frtti:Runtime Type Information Support,如果想支持 RTTI,那么可以在创建项目的时候勾选它。或者在module层的build.gradle文件中的cppFlags中添加-frtti标志,两者方法效果是一样的。
    • -fexcetions:Exceptions Support,有关C++的异常处理的支持,可以在创建项目的时候勾选,或者在 module层的build.gradle文件中的cppFlags中添加-fexcetions标志,两者方法效果是一样的。
    1. 添加CMakeList文件


    2. 添加native方法,并在在cpp目录下添加对应的实现

    两种实现native方法的方式

    jni中有两种实现native方法的方式,分别为静态注册和动态注册

    静态注册

    静态注册就是通过对应方法名的方式来实现native方法对应的c方法,方法命名规则:Java_类的全名_方法名
    例如

    //这是声明在Java文件中的方法
    public native void test();
    
    //这是对应在c中的方法名
    //我的包名是:com.demo.tianyl.jnidemo
    Java_com_demo_tianyl_jnidemo_MainActivity_test
    

    动态注册

    1. 首先需要定义一个结构体
    typedef struct {  
        const char* name; /*Java 中函数的名字*/  
        const char* signature; /*描述了函数的参数和返回值*/  
        void* fnPtr; /*函数指针,指向 C 函数*/  
    } JNINativeMethod;
    

    例如系统源码的写法

    static const JNINativeMethod gMethods[] = {
        {"setCamera",            "(Landroid/hardware/Camera;)V",    (void *)android_media_MediaRecorder_setCamera},
        {"setVideoSource",       "(I)V",                            (void *)android_media_MediaRecorder_setVideoSource},
        {"setAudioSource",       "(I)V",                            (void *)android_media_MediaRecorder_setAudioSource},
        ...
    };
    
    1. 编写注册方法,这里registerNativeMethods方法需要写在registerNatives之上
    static int registerNativeMethods(JNIEnv* env
            , const char* className
            , JNINativeMethod* gMethods, int numMethods) {
        jclass clazz;
        clazz = (*env)->FindClass(env, className);
        if (clazz == NULL) {
            return JNI_FALSE;
        }
        if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
            return JNI_FALSE;
        }
        return JNI_TRUE;
    }
    
    
    static int registerNatives(JNIEnv * env){
       if(!registerNativeMethods(env,JNIREG_CLASS,getMethods,sizeof(getMethods)/sizeof(getMethods[0])))
            return JNI_FALSE;
        return JNI_TRUE;
    }
    
    1. 实现JNI_OnLoad方法
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
        JNIEnv* env = NULL;
        jint result = -1;
        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            return -1;
        }
        assert(env != NULL);
        if (!registerNatives(env)) {//注册
            return -1;
        }
        result = JNI_VERSION_1_4;
        return result;
    }
    

    注册的方法就是从JNI_OnLoad方法中调用的,这里的JNI版本不需要写最新,只需要合适即可

    两者的优缺点

    一般来讲,静态注册是书写简单,但是由于方法名的对应关系,所以耦合性强,如果包名发生变化,那么需要大量修改,而且初次运行的效率也不如动态注册,所以如果没有特别愿意,推荐使用动态注册

    相关文章

      网友评论

          本文标题:NDK开发之JNI基础(2),静态注册和动态注册

          本文链接:https://www.haomeiwen.com/subject/vbzyeqtx.html