美文网首页
二、JNI函数动态注册和静态注册

二、JNI函数动态注册和静态注册

作者: ChiangCMBA | 来源:发表于2018-04-05 17:08 被阅读0次

    JNI函数的注册:将Java层的native函数和JNI层对应的实现函数关联起来。

    ===============================
    PushNative.java
    ===============================
    package com.fmtech.fmlive.jni;
    
    public class PushNative {
    
        static{
            System.loadLibrary("FMLive");
        }
    
        public native void setAudioOptions(int sampleRateInHz, int channel);
    
    }
    
    ===============================
    FMLive.c
    ===============================
    # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
    

    一、动态注册

    /*
    * 音频编码器配置
    * Class: com_fmtech_fmlive_jni_PushNative
    * Method: setAudioOptions
    * Signature: (II)V
    */
    //JNIEXPORT void JNICALL Java_com_fmtech_fmlive_jni_PushNative_setAudioOptions
    //Use dynamic register
    JNIEXPORT void JNICALL native_setAudioOptions(JNIEnv *env, jobject jobj, jint sampleRateInHz, jint numChannels){
            audio_encode_handle = faacEncOpen(sampleRateInHz, numChannels,&inputSamples,&maxOutputBytes);
            if(!audio_encode_handle){
                    LOGE("%s","-------open audio_encode failed");
                    return;
            }
    
            //设置音频编码参数
            faacEncConfigurationPtr p_config = faacEncGetCurrentConfiguration(audio_encode_handle);
            p_config->mpegVersion = MPEG4;
            p_config->allowMidside = 1;
            p_config->aacObjectType = LOW;
            p_config->outputFormat = 0; //输出是否包含ADTS头
            p_config->useTns = 1; //时域噪音控制,大概就是消爆音
            p_config->useLfe = 0;
            // p_config->inputFormat = FAAC_INPUT_16BIT;
            p_config->quantqual = 100;
            p_config->bandWidth = 0; //频宽
            p_config->shortctl = SHORTCTL_NORMAL;
    
            if(!faacEncSetConfiguration(audio_encode_handle, p_config)){
                    LOGE("%s","set faac encode configure failed");
                    return;
            }
            LOGI("%s","set faac encode configure success");
    
    }
    
    typedef struct {
        const char* name;
        const char* signature;
        void* fnPtr;
    } JNINativeMethod;
    
    static const JNINativeMethod gMethods[] = {
            {
                    "setAudioOptions",
                    "(II)V",
                    (void*)native_setAudioOptions
            }
    };
    
    static int registerNatives(JNIEnv* env)
    {
            LOGI("registerNatives begin");
            jclass clazz;
            clazz = (*env) -> FindClass(env, "com/fmtech/fmlive/jni/PushNative");
    
            if (clazz == NULL) {
                    LOGI("clazz is null");
                    return JNI_FALSE;
            }
    
            if ((*env) ->RegisterNatives(env, clazz, gMethods, NELEM(gMethods)) < 0) {
                    LOGI("RegisterNatives error");
                    return JNI_FALSE;
            }
    
            return JNI_TRUE;
    }
    
    
    jint JNI_OnLoad(JavaVM* vm, void* reserved){
            JNIEnv* env = NULL;
    
            if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
                    LOGI("ERROR: GetEnv failed\n");
                    return -1;
            }
            assert(env != NULL);
    
            registerNatives(env);
    
            return JNI_VERSION_1_4;//jni1.4以上支持
    }
    

    1、函数动态注册的好处:
    移植方便?

    2、使用数据结构JNINativeMethod来记录Java native方法和JNI函数的一一对应关系。

        typedef struct {
                  const char* name; //Java中native方法的名称,不用携带包的路径
                  const char* signature; //Java方法的签名信息,用字符串表示,是参数类型和返回值类型的组合
                  void* fnPtr; // JNI层对应函数的函数指针,注意它是void*类型
          } JNINativeMethod;
    

    3、当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_OnLoad的函数。如果有,就调用它,而动态注册的工作就是在这里完成的。
    所以,如果想使用动态注册方法,就必须实现JNI_OnLoad函数,只有在这个函数中才有机会完成动态注册的工作。

    二、静态注册

    JNIEXPORT void JNICALL Java_com_fmtech_fmlive_jni_PushNative_setAudioOptions
    (JNIEnv *env, jobject jobj, jint sampleRateInHz, jint numChannels){
            audio_encode_handle = faacEncOpen(sampleRateInHz, numChannels,&inputSamples,&maxOutputBytes);
            if(!audio_encode_handle){
                    LOGE("%s","-------open audio_encode failed");
                    return;
            }
    
            //设置音频编码参数
            faacEncConfigurationPtr p_config = faacEncGetCurrentConfiguration(audio_encode_handle);
            p_config->mpegVersion = MPEG4;
            p_config->allowMidside = 1;
            p_config->aacObjectType = LOW;
            p_config->outputFormat = 0; //输出是否包含ADTS头
            p_config->useTns = 1; //时域噪音控制,大概就是消爆音
            p_config->useLfe = 0;
            // p_config->inputFormat = FAAC_INPUT_16BIT;
            p_config->quantqual = 100;
            p_config->bandWidth = 0; //频宽
            p_config->shortctl = SHORTCTL_NORMAL;
    
            if(!faacEncSetConfiguration(audio_encode_handle, p_config)){
                    LOGE("%s","set faac encode configure failed");
                    return;
            }
            LOGI("%s","set faac encode configure success");
    
    }
    

    当Java层调用setAudioOptions 函数时,它会从对应的JNI库中寻找Java_com_fmtech_fmlive_jni_PushNative_setAudioOptions函数,如果没有,就会报错。如果找到,则会为这个setAudioOptions 和Java_com_fmtech_fmlive_jni_PushNative_setAudioOptions建立一个关联关系,其实就是保存JNI层函数的函数指针。以后再调用setAudioOptions函数时,直接使用这个函数指针就可以了,当然这项工作是由虚拟机完成的。
    函数静态注册就是根据函数名来建立Java函数和JNI函数之间的关联关系的,而且它要求JNI层函数的名字必须遵守特定的格式。
    函数静态注册的弊端:
    1、需要编译所有声明了native方法的Java类,每个所生成的class文件 都得用javah生成一个头文件。
    2、javah生成的JNI层函数名特别长,书写起来很不方便。
    3、初次调用native函数时要根据函数名字搜索对应用JNI层函数来建立关联关系,这样会影响运行效率。

    相关文章

      网友评论

          本文标题:二、JNI函数动态注册和静态注册

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