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

2018-04-05  本文已影响0人  ChiangCMBA

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层函数来建立关联关系,这样会影响运行效率。

上一篇下一篇

猜你喜欢

热点阅读