原文网址: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方法呢?就需 ...
随机推荐
-
SQLSERVER如何使用递增排序的GUID做主键
场景: 产品表数据量较大想用Guid做表的主键,并在此字段上建立聚簇索引. 因为Guid是随机生成的,生成的值大小是不确定的,每次生成的数可能很大,也可能很小.这样会影响插入的效率 1.NEWSEQU ...
-
Oracle客户端简易连接报错ORA-12154,TNS-03505
环境: 服务端:RHEL6.5 + Oracle Server 11.2.0.4 客户端:Win2003 + Oracle Client 10.2.0.1 1.问题现象 2.Troubleshooti ...
-
libsvm使用详细说明
一,简介 LibSVM是*林智仁(Chih-Jen Lin)教授2001年开发的一套支持向量机的库,这套库运算速度还是挺快的,因此成为目前国内应用最多的SVM的库.详细的使用说明及博主博客见下链接: ...
-
多线程问题(JVM重排序)
public class Test3 { private static boolean ready; private static int Number; private static class R ...
-
python3.x元组打印错误 TypeError: unsupported operand type(s) for %: &#39;NoneType&#39; and &#39;tuple&#39;
原创by南山南北秋悲 欢迎引用!请注明原地址:http://www.cnblogs.com/hwd9654/p/5676746.html 谢谢! TypeError: unsupported ope ...
-
利用VGG19实现火灾分类(附tensorflow代码及训练集)
源码地址 https://github.com/stephen-v/tensorflow_vgg_classify 1. VGG介绍 1.1. VGG模型结构 1.2. VGG19架构 2. 用Ten ...
-
Java内存管理及对Java对象管理
Java内存管理及对Java对象管理 1Java内存管理 1.1Java中的堆和栈 通常来说,人们会将Java内存氛围栈内存(Stack)和堆内存(Heap). 栈内存用来保存基本类型的变量和对象的引 ...
-
教你如何用Meterpreter渗透Win系统
在这篇文章中,我们将跟大家介绍如何使用Meterpreter来收集目标Windows系统中的信息,获取用户凭证,创建我们自己的账号,启用远程桌面,进行屏幕截图,以及获取用户键盘记录等等. 相关Payl ...
-
JMeter中用java修改文件名称
import java.io.File; String NewDataPath=bsh.args[0]; File SrcFile= new File(NewDataPath+"AutoTe ...
-
soapUI-DataSource
1.1.1.1 概述 - 数据源 Option Description Properties DataSource属性表 Toolbar DataSource工具栏 Configura ...