前面介绍了那个结构体,为这一篇文章做了铺垫,这一篇进一步介绍so在整个加载过程中的流程.
当android的VM(虚拟机)调用System.LoadLibrary函数去加载.so文件时,首先会调用JNI_OnLoad函数。
这个函数有两个功能:
1. 告诉VM,该C组件使用哪一个版本的JNI。如果so文件没有使用JNI_OnLoad,则VM默认c组件使用最老的jni 1.1的版本。由于新版本的jni有许多新的功能,如果是默认1.1最老的版本的话,则不能使用新功能。
---------
是不是应该在JNI_OnLoad中设置版本号?如果JNI_OnLoad中没有这个过程,是不是还是没有发挥作用?
(透过返回值来设置,例如return JNI_VERSION_1_4;)
2. c组件的开发者可以在JNI_OnLoad这里进行c组件的一些初始化的工作。因为在加载so文件时,首先会去调用这个JNI_OnLoad函数
与JNI_OnLoad 对应的有 JNI_OnUnload.
JNI_OnUnload做对应的清理工作。
==============
在实际的项目,经常会有从android 上层透过jni调用c组件的code,或者在c组件中透过jni调用android上层的code。
要实现这两个目的,就要搞清楚系列的流程。
一 先总结从android 上层透过jni调用c组件的流程
有两种方法。
二 在c组件中透过jni调用android上层
====
IP 3088项目的jni部分流程
jni_onLoad 调用
TcMediaEngineRegisterNativeMethods(venv); //实现从android 上层透过jni调用c组件的code
TcMediaEngineRegisterNativeMethods中调用
TcMediaEngineResolveJavaClass(env); //在c组件中透过jni调用android上层的code。
TcMediaEngineRegisterNativeMethods的具体实现
1.定义 本地方法 和 上层方法 的映射表
static JNINativeMethod sMethods[] =
{
{"Init", "()I",(void *)native_media_engine_core_Init},
{"Stop", "()I",(void *)native_media_engine_core_stop},
{"Echo", "(I)I",(void *)native_media_engine_core_echo},
{"StartTone","(I)I",(void *)native_media_engine_core_starttone},
{"StopTone","()I",(void *)native_media_engine_core_stoptone},
{"setVideoWindowId","(Ljava/lang/Object;)V",(void *)native_media_engine_core_set_video_windowid},
{"setVideoPreviewWindowId","(Ljava/lang/Object;)V",(void *)native_media_engine_core_set_video_preview_windowid},
{"PlayDTMF", "(IC)I",(void *)native_media_engine_core_play_dtmf},
{"StopDTMF", "(I)I",(void *)native_media_engine_core_stop_dtmf},
{"SetRingFile", "(Ljava/lang/String;)V",(void *)native_media_engine_core_set_ringing_file},
{"SetRingbackFile", "(Ljava/lang/String;)V",(void *)native_media_engine_core_set_ringback_file},
{"MuteMIC", "(I)V",(void *)native_media_engine_core_mute_mic},
{"SetAGC", "(I)V",(void *)native_media_engine_core_set_agc},
{"SetJitterBuffer", "(II)V",(void *)native_media_engine_core_set_jitterbuffer},
{"SetVideoSize", "(II)V",(void *)native_media_engine_core_set_videosize},
{"SetPlaybackGain", "(F)V",(void *)native_media_engine_core_set_play_gain},
{"GetVideoDevice", "()I",(void *)native_media_engine_core_get_video_device},
{"SetVideoDevice", "(I)V",(void *)native_media_engine_core_set_video_device},
{"UpdateVideoCamera" , "()V",(void *)native_media_engine_core_update_video_camera},
};
2 做初始化工作
const char* classPathName="com/tecom/v2ware/MediaEngineCoreManager";
int iSize=sizeof(sMethods)/sizeof(sMethods[0]);
3. 通过jni 环境变量找到上层对应的java class
clazz = env->FindClass(classPathName);
if(!clazz)
{
ME_LOGE("'%s' is not found.", classPathName);
return;
}
4. 透过jni 环境变量 注册本地方法
if(env->RegisterNatives(clazz,sMethods,iSize) < 0)
{
ME_LOGE("TcMediaEngineRegisterNativeMethods failed for '%s'", classPathName);
return ;
}
TcMediaEngineResolveJavaClass 的具体实现
1. 透过jni环境变量找到 java 类
theclass = env->FindClass(AndroidApi5WrapperPath);
2. 透过jni环境变量和上一步得到的class ,进一步得到要调用的方法
jmethodID method = env->GetStaticMethodID(theclass ,"selectNearestResolutionAvailable", "(III)[I");
3. 透过jni环境变量和上一步得到的method ,调用方法
jobject resArray = env->CallStaticObjectMethod(helperClass, method, ((AndroidWebcamConfig*)d->webcam->data)->id, d->requestedSize.width, d->requestedSize.height);