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

AndroidNDK开发(5)————Android JNI层实现文件的read、writeseek操作 /******************************************************************************************** * author:conowen@大钟 * E-mail:conowen@hotmail.com ... 阅读详情

/********************************************************************************************
 * author:conowen@大钟                                                                                                                          
 * E-mail:conowen@hotmail.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 开发 Android NDK 开发 Android NDK 简介 NDK 全称 Native Development Kit 本地语言(C/C++)开发包。而对应的Android开发中使用的是 Android-SDK,(Software Development Kit)软件开发包(只支持Java语言开发)。我们可以利用 NDK 将 C/C++ 开发的代码编译成 so 库,让 Java 程序调用Java 使用的是 JNIJava本地接口) 来调用NDK 开发优点: 利用 NDK 开发的库,不容易被反编译, 阅读详情

相关推荐

【转】ERROR: JDWP Unable to get JNI 1.2 environment解决办法

当程序启动运行时突然出现如下的异常: ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2 JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [util.c:840] 原因: 上次启动调试的代码有错误,导致进程没有终止,占用了Console输出,在...

weixin_45814795的博客 2043

仿佛找到了方向

1980年,因高考语文一分之差,唐骏没有考上理想的大学和专业,这时的唐骏有些失落苦闷,甚至有些自暴自弃;1985年的唐骏不仅找到自己的终身伴侣,还考上了热门专业的研究生,并靠着努力“钻营”获得留学日本的机会; 1990年,唐骏赴美攻读博士,并先后创立了三家小型公司; 1994年,抱着“偷师学艺”的本意,唐骏放弃自己的“皮包公司”,加入微软成为一名软件工程师; 1997年,时任微软总部W

BEGIN DECLARE 922

Android NDK 开发(二)JNI 传递参数和返回值

前言我们在使用 JNI 时最常问到的是 JAVA 和 C/C++之间如何传递数据,以及数据类型之间如何 互相映射。我们从整数等基本类型和数组、字符串等普通的对象类型开始讲述。至于如何传递任意对象,将在后面会更新。原文链接请标明: http://blog.csdn.net/u011974987/article/details/52743495 本文出自:【stromxu的博客】正文继JNI简介

stromxu 的专栏 2万+

象幽灵一样活着

象幽灵一样活着我怀念那个独自吃着冰淇淋若无旁人坐在喧闹大街的傍晚。 那个灰色的傍晚,只有絮絮的音乐夹杂在飞舞的雪花中。路旁通亮的灯箱上面印着各式的广告,街上的霓虹闪烁在路人的脸上。每一次走过这里,都以那种悠然自得的姿态。 人是个很奇怪的动物,总在一些时候莫名其妙的想些不着边际的事情,每次走在这条街上,总能让自己的思绪慢慢的飘起来,随风不知去了何处,突然感觉自己犹如幽灵一样,漂在这个城市里,看着

u013568834的专栏 744

[31期] 找回失去的自己

  找回失去的自己   来到兄弟连一个多星期了,感觉时间过的很慢.但却像那时候的高中---煎熬着却充实着.在山东上着大学,学着自己不喜欢的专业,一恍就是三年 过去了.下半年大四了.我却没想再去准备考研的事.其过着漫无目标的大四不如朝着自己的方向迈出一步.也许下一站才是我想要的生活.   打理好学校的事,坐上了一路北上的列车来到北京.还记得那天到北京西站的时候已经是下午三点多了,北京下雨了...

web开发 137

回想2020,仿佛就在昨天

回想2020,仿佛就在昨天

GEEK-BANANA 491

被裁员后,我用6个月重新定义程序员价值,找到了转型的方向!

那个被裁的下午,多年的代码生涯仿佛成了笑话

程序员若风的博客 1148

我仿佛成了那个差不多先生

前天,学校毕设答辩完,走出答辩考场,我的心如死灰,在讲台上的时候被一群老师怼得整个人都懵了。我现在理理思绪,记录下这次糟糕的经历。答辩在前,因为之前论文的一些问题,不停的修改,甚至答辩前两天一直通宵,累得身心俱疲,一般来讲,如果东西完全是我做的,写论文应该是很快的,问题是做的是一个安卓平台的管理系统,后台却有一个高大上的名字,什么高性能计算,集群之类的,结果导致,我论文的背景跟现状一直写不好,前前

CrazyCosin的博客 750

方向不对,努力白费(好文章)

方向不对,努力白费   这个世界有一种鸡血,用来让你去拼命——拼命的工作、学习,为一个更高的分数,为一个更高的工资,得到更多的认可。但是如果问,这一切的意义在哪里,你知道吗?      因为没有最好只有更好,因为只有第一名才能被人记住,因为梦想,因为奋斗。      这些话本没有错,但当努力只是为了努力,当奋斗只是为了奋斗,这一切就像一个奇怪的自我催眠,亲,你知道

梧桐下的四叶草 1851

北漂七八年,有得亦有失,只有回到家才能找到归属感幸福!

栽一棵树最好的时间是10年前,其次是现在,加油吧!在谋生的路上,请记得热爱生活!

吴秋霖的博客 7438

我寻找的方向

努力吧,少年!!

永不秃头的程序员的博客 1053

程序员如何选择技术方向

程序员如何选择技术方向 池建强12月09日 17:53 分享到: 程序员 技术方向 分类 : 互联网 摘要 : 程序员三部曲之第三篇「程序员如何选择技术方向」——两个励志小故事 http://chijianqiang.baijia.baidu.com/article/39179 最近写了「当程序员老去」「程序员真正的价值」两篇文章,传播甚...

weixin_34202952的博客 1339

[转载]辜新星:时刻调整方向 找到人生的蓝海

sunlight 2010-03-28 18:33:40 我想知道流星的美丽 是否值得去寻求 于是我心狂奔 羽化成黑夜的彩虹 蜕变成月光的清风 幸福了我很久 —— 郑钧《流星》 引子:骑单车的故事 小时候看过一个关于单车的故事:爸爸教儿子骑单车,儿子总也学不会。爸爸看出了问题所在,于是在再一次示范后让儿子看身后弯弯曲曲的车轮印,并语重心长地说:我儿,虽然你看见别人骑车时车头...

weixin_30648963的博客 279

我和云栖有个约会 - 仿佛是一个穿越的体验

从最早的云计算到如今的数据智能,从飞天操作系统到城市大脑,云栖大会一直在云栖小镇传递创新火种,描绘着计算技术的未来。此外,大会还提供了丰富的动手实践机会,参者可以多元技术互动,边学边用,深入探索云栖大会所呈现的内容。大会聚焦于云上的实际应用,通过实干家的务实分享,让参者能够获取有关云计算和人工智能的创新经验。云栖大会以开放的姿态吸引着云计算产业的众多参者,通过丰富的形式和内容,分享实践经验,推动科技创新的能力,并展示了科技平台展会上的各种创新成果。它的目标是启发社会、企业和每个人的科技创新能力。

全栈若城,专注知识分享 1877
上一篇: Android多媒体开发(2)————使用Android NKD编译原版FFmpeg
下一篇: Android的NDK开发(2)————利用Android NDK编写一个简单的HelloWorld
conowen
博客等级 码龄17年 1053粉丝 55原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值