【转】 Android的NDK开发(1)————Android JNI简介与调用流程
原文网址:http://blog.csdn.net/conowen/article/details/7521340
********************************************************************************************
* author:conowen@大钟
* E-mail:conowen@hotmail.com
*site:http://menwoo.com/
*深圳市大望谷科技有限公司
* http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
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的版本。如
- result = JNI_VERSION_1_4;
当没有JNI_OnLoad()函数时,Android调试信息会做出如下提示(No JNI_OnLoad found)
- 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为例详细说一下:
- //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;
- }
- //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简介与调用流程的更多相关文章
- Android的NDK开发(1)————Android JNI简介与调用流程
1.JNI简介 JNI全称为Java Native Interface(Java本地调用).从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C ...
- Android的NDK开发(5)————Android JNI层实现文件的read、write与seek操作
1. 在Android的Java层实现文件的读写操作是非常简单的,可以参看之前写的博文:http://blog.csdn.net/conowen/article/details/7296121 在JN ...
- Android之NDK开发(转)
Android之NDK开发 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...
- Android之NDK开发
转自:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C ...
- 【转】Android之NDK开发
原文网址:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支 ...
- Android之NDK开发(转载)
http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C+ ...
- 【Android】Android Studio NDK 开发
Android Studio NDK 开发 记录在Android Studio中NDK简单开发的步骤 用到的Android Studio版本为3.5. 配置NDK 下载NDK 一般在SDK下已经有自带 ...
- Android下NDK开发环境搭建
Android下NDK开发环境搭建 1. AndroidNDK安装与配置 1.1 NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP部 ...
- Android Studio NDK开发-JNI调用Java方法
相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如callback,系统信息等.... 如何在JNI中调用Java方法呢?就需 ...
随机推荐
- 配置Nginx 1.8支持PHP 5.6
启动PHP和Nginx 修改Nginx配置文件/usr/local/nginx/conf/nginx.conf server { listen ; server_name localhost; loc ...
- js分家效应
(原创文章,转载请注明出处) 有继承,那么就有分家.让我们看以下例子. var parents = function(){ } parents.prototype.money = 200; var c ...
- js实现图片自动切换效果。
js实现图片自动切换效果,简单实用,原谅我只是一只小菜鸟还在学大神天天写博文装逼. <script language="javascript"> setInterval ...
- java可变参数Varargs
http://www.cnblogs.com/shishm/archive/2012/01/31/2332656.html J2SE 1.5提供了“Varargs”机制.借助这一机制,可以定义能和多个 ...
- Hash - a javascript dictionary object.
Hash,in wikipedia, may relevant to many stuffs. In javascript, hash is a group of name/value pairs w ...
- ssh框架配置事务管理器
http://blog.163.com/zsq303288862@126/blog/static/9374596120111182446727/
- CSRF 攻击的应对之道
转载自imb文库 CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在 ...
- 添加Appicon的方法
1.将设计好的图片,拖拽到Groups&Files的Resources目录下: 2.修改Resources目录下的“工程名-info.plist”文件 3.修改该文件的Iconfile属性,填 ...
- java基础易错点总结(一)
子类继承父类表示子类比他的父类包含更多的信息和方法 子类调用重载的构造方法时会调用父类的构造方法,super();一般如果不写的话会隐式的调用,而且每次调用都在所有语句之前. 在函数中,使用父类的地方 ...
- 一种实现C++反射功能的想法(二)
在介绍我的思路前, 让我们准备下预备知识 C++是怎么实现类函数的绑定的. 我们知道类的非静态成员函数是存储在全局区, 并在内存中只保存一份副本. 我们调用非静态成员函数是通过类对象进行调用. 那么如 ...