二、JNI函数动态注册和静态注册
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层函数来建立关联关系,这样会影响运行效率。