【转】 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方法呢?就需 ...
随机推荐
- Android(java)学习笔记213:开源框架post和get方式提交数据(qq登录案例)
1.前面提到Http的get/post方式 . HttpClient方式,实际工作的时候不常用到,因为这些方式编写代码是很麻烦的 2.Android应用会经常使用http协议进行传输,网上会有很完善 ...
- 别人走的路--uap
首先,我先谈谈我个人的经历,我今年34岁了,做了10多年的ERP实施顾问,大学刚毕业的时候是做ERP软件开发的,后来转岗做了实施顾问.根据我的个人经验,我给你几点建议.1.既然是很大的公司,那么ERP ...
- discuz论坛几种安全策略(一)
安全问题 最近公司准备搭建一个discuz论坛,大头让我调研一下discuz的安全策略,并提出如下几点要求: 1.防止php上传漏洞2.防止大量刷新攻击限制某个IP大量刷新某一页面导致论坛宕机3.防止 ...
- for循环,如何结束多层for循环
采用标签方式跳出,指定跳出位置, a:for(int i=0;i<n;i++) { b:for(int j=0;j<n;j++) { if(n=0) { break a; } } }
- left join 和 left outer join 有什么区别?
left join 是left outer join的简写,left join默认是outer属性的.outer join则会返回每个满足第一个(顶端)输入与第二个(底端)输入的联接的行.它还返回任何 ...
- golang bufio writer,reader 缓存规则
读,写,缓冲区可以杜绝频繁的读,写动作1.写缓存,如果一次write的长度大于buffer长度那么久发送当前缓冲区的内容并且发送要写入的内容,就是不在缓存了.如果发送的内容小于buffer长度,就按缓 ...
- Oracle数据库之视图与索引
Oracle数据库之视图与索引 1. 视图简介 视图是基于一个表或多个表或视图的逻辑表,本身不包含数据,通过它可以对表里面的数据进行查询和修改. 视图基于的表称为基表,视图是存储在数据字典里的一条SE ...
- nginx 编译选项
内容有些多,一眼看来难免头昏脑胀,但坚持看完,相信你一定会有所收获. nginx参数: --prefix= 指向安装目录 --sbin-path 指向(执行)程序文件(nginx) --conf-pa ...
- python自动开发之第十八天
一.JS正则 test - 判断字符串是否符合规定的正则 rep = /\d+/; rep.test("asdfoiklfasdf89asdfasdf") # true rep = ...
- Python 环境
文章出处:http://www.cnblogs.com/winstic/,请保留此连接 总结一下自己使用python过程中安装三方包的方法 Python 安装 Python的安装非常简单,本人使用的w ...