【转】 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方法呢?就需 ...
随机推荐
- 自己写的demo。List<HashMap<String,Object>>=new ArrayList<HashMap<String,Object>>
package com.pb.collection; import java.util.ArrayList; import java.util.HashMap; import java.util.It ...
- tomcat发布项目时,空文件夹未发布成功
问题背景: 项目发布到服务器时,缺少文件夹,到时向此文件夹写数据时发生错误. 后来经查,缺少这个文件夹,项目部署发布时,并不会把空文件夹发布上去 解决: 1.在空文件中加入,一个文件.就可以发布成功 ...
- UTF-8和GBK有什么区别?
字符均使用双字节来表示,只不过为区分中文,将其最高位都定成1. 至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码.对于英文字符 ...
- GUID是什么意思及Guid在sqlserver中的使用
GUID(全球唯一标识)是微软使用的一个术语,由一个特定的算法,给某一个实体,如Word文档,创建一个唯一的标识,GUID值就是这个唯一的标识码.GUID广泛应用于微软的产品中,用于识别接口.复制品. ...
- 段落排版--缩进(text-indent)
中文文字中的段前习惯空两个文字的空白,这个特殊的样式可以用下面代码来实现: p{text-indent:2em;} <p>1922年的春天,一个想要成名名叫尼克卡拉威(托比?马奎尔Tobe ...
- Core Animation之CAKeyframeAnimation
在上一篇专题文章中我们学习了iOS核心动画CoreAnimation中CABasicAnimation动画的使用方法.CABasicAnimation已经可以应付一些比较简单的应用场景了,比如view ...
- KTV2
自制KTV点歌系统经验 重唱与切歌 1.重唱 这个简单,会了播放,就会了这个; 我们用的数组下标来确定播放歌曲,自然如果下标没变的话,那播放的就还是这首了.所以只要确定了要执行的命令是重唱就行了. 那 ...
- linux防火墙解封某端口
首先,使用netstat –tunlp查看是否23端口被防火墙封掉了: 再使用iptables修改设置, # iptables -I INPUT -p tcp --dport 23 –jACCEPT ...
- js字符串 数字 的转换
js 字符串转化成数字 的 三种方法主要有 转换函数.强制类型转换.利用js变量弱类型转换. 1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数 ...
- IE 6最小最大宽度与高度的写法
最小最大宽度,最小最大高度,这是CSS很常见的一个要求.在现代浏览器中,一个 min-height,min-width 就可以解决问题,但是在IE系列,比如IE6则比较繁琐一点.下面总结一些IE 6下 ...