1、JNI简介

JNI全称为Java Native Interface(Java本地调用)。从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C++)进行交互。并非从Android发布才引入JNI的概念的。

2、JNI与NDK

        简单来说,Android的NDK提供了一些交叉编译工具链和Android自带的库,这些Android的库可以让开发者在编写本地语言的程序时调用。而NDK提供的交叉编译工具链就对已经编写好的C&C++代码进行编译,生成库。

        当然了,你也可以自己搭建交叉编译环境,而不用NDK的工具和库。然后生成库,只要规范操作,一样可以生成能让JAVA层成功调用的库文件的。

       利用NDK进行编译本地语言可以参考这篇博文:http://blog.csdn.NET/conowen/article/details/7522667

3、JNI  调用流程

         众所周知,Android的应用层的类都是以Java写的,这些Java类编译为Dex文件之后,必须靠Dalvik虚拟机( Virtual Machine)来执行。假如在执行java程序时,需要载入C&C++函数时,Dalvik虚拟机就会去加载C&C++的库,(System.loadLibrary("libName");)让java层能顺利地调用这些本地函数。需要清楚一点,这些C&C++的函数并不是在Dalvik虚拟机中运行的,所以效率和速度要比在Dalvik虚拟机中运行得快很多。

       Dalvik虚拟机成功加载库之后,就会自动地寻找库里面的JNI_OnLoad函数,这个函数用途如下:

(1)告诉Dalvik虚拟机此C库使用哪一个JNI版本。如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本。但是新版的JNI做了很多的扩充,也优化了一些内容,如果需要使用JNI的新版功能,就必须在JNI_OnLoad()函数声明JNI的版本。如

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片 result = JNI_VERSION_1_4; 当没有JNI_OnLoad()函数时,Android调试信息会做出如下提示(No JNI_OnLoad found) [java] view plain copy
print?在CODE上查看代码片派生到我的代码片 04-29 13:53:12.184: D/dalvikvm(361): Trying to load lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
04-29 13:53:12.204: D/dalvikvm(361): Added shared lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
04-29 13:53:12.204: D/dalvikvm(361): No JNI_OnLoad found in /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98, skipping init (2)因为Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,所以我们可以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。注册本地函数,可以加快java层调用本地函数的效率。 另外:与JNI_OnLoad()函数相对应的有JNI_OnUnload()函数,当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。 4、例子(关于jni里面的数据类型转换与常用jni方法下一篇博文介绍) 下面以havlenapetr的FFmpeg工程里面的onLoad.cpp为例详细说一下:
[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片 //onLoad.cpp文件 #define TAG "ffmpeg_onLoad" #include <stdlib.h>
#include <android/log.h>
#include "jniUtils.h" extern "C" { extern int register_android_media_FFMpegAVRational(JNIEnv *env); #ifdef BUILD_WITH_CONVERTOR
extern int register_android_media_FFMpeg(JNIEnv *env);
#endif extern int register_android_media_FFMpegAVFormatContext(JNIEnv *env);
extern int register_android_media_FFMpegAVInputFormat(JNIEnv *env); } extern int register_android_media_FFMpegAVCodecContext(JNIEnv *env);
extern int register_android_media_FFMpegUtils(JNIEnv *env);
extern int register_android_media_FFMpegAVFrame(JNIEnv *env); #ifdef BUILD_WITH_PLAYER
extern int register_android_media_FFMpegPlayerAndroid(JNIEnv *env);
#endif static JavaVM *sVm; /*
* Throw an exception with the specified class and an optional message.
*/
int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
jclass exceptionClass = env->FindClass(className);
if (exceptionClass == NULL) {
__android_log_print(ANDROID_LOG_ERROR,
TAG,
"Unable to find exception class %s",
className);
return -1;
} if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR,
TAG,
"Failed throwing '%s' '%s'",
className, msg);
}
return 0;
} JNIEnv* getJNIEnv() {
JNIEnv* env = NULL;
if (sVm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR,
TAG,
"Failed to obtain JNIEnv");
return NULL;
}
return env;
} /*
* Register native JNI-callable methods.
*
* "className" looks like "java/lang/String".
*/
int jniRegisterNativeMethods(JNIEnv* env,
const char* className,
const JNINativeMethod* gMethods,
int numMethods)
/*从com_media_ffmpeg_FFMpegPlayer.cpp文件跳到此,完成最后的注册
* 向 Dalvik虚拟机(即AndroidRuntime)登记传过来的参数gMethods[]所含的本地函数
*/
{
jclass clazz; __android_log_print(ANDROID_LOG_INFO, TAG, "Registering %s natives\n", className);
clazz = env->FindClass(className);
if (clazz == NULL) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "Native registration unable to find class '%s'\n", className);
return -1;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "RegisterNatives failed for '%s'\n", className);
return -1;
}
return 0;
}
//Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;//定义JNI Env
jint result = JNI_ERR;
sVm = vm;
/*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);
* GetEnv()函数返回的 Jni 环境对每个线程来说是不同的,
* 由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时,
* 所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取
*
*/
//得到JNI Env
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "GetEnv failed!");
return result;
} __android_log_print(ANDROID_LOG_INFO, TAG, "loading . . ."); /*开始注册
* 传入参数是JNI env
* 由于下面有很多class,只以register_android_media_FFMpegPlayerAndroid(env)为例子
*/ #ifdef BUILD_WITH_CONVERTOR
if(register_android_media_FFMpeg(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpeg");
goto end;
}
#endif if(register_android_media_FFMpegAVFormatContext(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFormatContext");
goto end;
} if(register_android_media_FFMpegAVCodecContext(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVCodecContext");
goto end;
} if(register_android_media_FFMpegAVRational(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVRational");
goto end;
} if(register_android_media_FFMpegAVInputFormat(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVInputFormat");
goto end;
} if(register_android_media_FFMpegUtils(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegUtils");
goto end;
} if(register_android_media_FFMpegAVFrame(env) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFrame");
goto end;
} #ifdef BUILD_WITH_PLAYER
if(register_android_media_FFMpegPlayerAndroid(env) != JNI_OK) {//跳到----》com_media_ffmpeg_FFMpegPlayer.cpp文件
__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegPlayerAndroid");
goto end;
}
#endif __android_log_print(ANDROID_LOG_INFO, TAG, "loaded"); result = JNI_VERSION_1_4; end:
return result;
} [cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片 //com_media_ffmpeg_FFMpegPlayer.cpp文件
/*
*
* 由于代码量较大,com_media_ffmpeg_FFMpegPlayer.cpp开始的一部分省略,只是贴出注册函数的相关部分。
*/
static const char* const kClassPathName = "com/media/ffmpeg/FFMpegPlayer";
/*
* 由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,
* 可多次调用registerNativeMethods()函数来更换本地函数的指针,
* 从而达到弹性调用本地函数的目的。
*/
static JNINativeMethod gMethods[] = {
{"setDataSource", "(Ljava/lang/String;)V", (void *)com_media_ffmpeg_FFMpegPlayer_setDataSource},
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)com_media_ffmpeg_FFMpegPlayer_setVideoSurface},
{"prepare", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_prepare},
{"_start", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_start},
{"_stop", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_stop},
{"getVideoWidth", "()I", (void *)com_media_ffmpeg_FFMpegPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)com_media_ffmpeg_FFMpegPlayer_getVideoHeight},
{"seekTo", "(I)V", (void *)com_media_ffmpeg_FFMpegPlayer_seekTo},
{"_pause", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_pause},
{"isPlaying", "()Z", (void *)com_media_ffmpeg_FFMpegPlayer_isPlaying},
{"getCurrentPosition", "()I", (void *)com_media_ffmpeg_FFMpegPlayer_getCurrentPosition},
{"getDuration", "()I", (void *)com_media_ffmpeg_FFMpegPlayer_getDuration},
{"_release", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_release},
{"_reset", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_reset},
{"setAudioStreamType", "(I)V", (void *)com_media_ffmpeg_FFMpegPlayer_setAudioStreamType},
{"native_init", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)com_media_ffmpeg_FFMpegPlayer_native_setup},
{"native_finalize", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_native_finalize},
{"native_suspend_resume", "(Z)I", (void *)com_media_ffmpeg_FFMpegPlayer_native_suspend_resume},
}; int register_android_media_FFMpegPlayerAndroid(JNIEnv *env) {
return jniRegisterNativeMethods(env, kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
/*跳到OnLoad.cpp文件中的
* jint jniRegisterNativeMethods(JNIEnv* env,
const char* className,
const JNINativeMethod* gMethods,
int numMethods) */
}

Android的NDK开发(1)————Android JNI简介与调用流程的更多相关文章

  1. 【转】 Android的NDK开发(1)————Android JNI简介与调用流程

    原文网址:http://blog.csdn.net/conowen/article/details/7521340 ****************************************** ...

  2. Android的NDK开发(5)————Android JNI层实现文件的read、write与seek操作

    1. 在Android的Java层实现文件的读写操作是非常简单的,可以参看之前写的博文:http://blog.csdn.net/conowen/article/details/7296121 在JN ...

  3. Android之NDK开发(转)

    Android之NDK开发 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...

  4. Android之NDK开发

    转自:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C ...

  5. 【转】Android之NDK开发

    原文网址:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支 ...

  6. Android之NDK开发(转载)

    http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C+ ...

  7. Android下NDK开发环境搭建

    Android下NDK开发环境搭建 1.     AndroidNDK安装与配置 1.1  NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP部 ...

  8. 【Android】Android Studio NDK 开发

    Android Studio NDK 开发 记录在Android Studio中NDK简单开发的步骤 用到的Android Studio版本为3.5. 配置NDK 下载NDK 一般在SDK下已经有自带 ...

  9. Android Studio NDK开发-JNI调用Java方法

    相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如callback,系统信息等.... 如何在JNI中调用Java方法呢?就需 ...

随机推荐

  1. Vue render: h => h(App) $mount

    $mount()手动挂载 当Vue实例没有el属性时,则该实例尚没有挂载到某个dom中: 假如需要延迟挂载,可以在之后手动调用vm.$mount()方法来挂载.例如: new Vue({ //el: ...

  2. MYSQLMANAGER实例管理器总结

    好久没有写文章了,今天来看看MYSQL的实例管理器(MYSQLMANAGER).一.简单介绍:1.MySQL实例管理器(IM)是通过TCP/IP端口运行的后台程序,用来监视和管理MySQL数据库服务器 ...

  3. 为什么golang的开发效率高(编译型的强类型语言、工程角度高、在开发上的高效率主要来自于后发优势,编译快、避免趁编译时间找产品妹妹搭讪,既是强类型语言又有gc,只要通过编译,非业务毛病就很少了)

    作者:阿猫链接:https://www.zhihu.com/question/21098952/answer/21813840来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...

  4. imageView-scaleType 图片压缩属性

    今天用到了图片压缩的属性,自己参照网上的说明,验证了一下,截图如下 (1)当图片背景是方形的时候 代码如下 <LinearLayout android:id="@+id/l31&quo ...

  5. Flume Interceptors官网剖析(博主推荐)

    不多说,直接上干货! Flume Sources官网剖析(博主推荐) Flume Channels官网剖析(博主推荐) Flume Channel Selectors官网剖析(博主推荐) Flume ...

  6. visual studio code 中 debugger for chrome 插件的配置

    安装 debugger for chrome 插件后,把默认的 launch.json 改成: { "name": "谷歌浏览器", "type&qu ...

  7. WebService学习总结(2)——WebService是什么?

    一.WebService是什么? 1. 基于Web的服务:服务器端整出一些资源让客户端应用访问(获取数据) 2. 一个跨语言.跨平台的规范(抽象) 3. 多个跨平台.跨语言的应用间通信整合的方案(实际 ...

  8. 洛谷 P1808 单词分类_NOI导刊2011提高(01)

    P1808 单词分类_NOI导刊2011提高(01) 题目描述 Oliver为了学好英语决定苦背单词,但很快他发现要直接记住杂乱无章的单词非常困难,他决定对单词进行分类. 两个单词可以分为一类当且仅当 ...

  9. crm翻译导航栏

    在crm里面怎样翻译导航栏? 过程例如以下: 1 先新建一个解决方式.把网站地图加进去 2: 然后把这个解决方式到出来来,解压文件: 3:编辑第二个文件: watermark/2/text/aHR0c ...

  10. ejs模板引擎的使用

    引入ejs.min.js 创建模板,以<%=jsCode%>包裹起来其余的html和html结构一样 focusTemplateData是模板使用的数据,使用$.each()方法遍历绑定数 ...