上一篇教程中,我针对一个最简单的HelloWorld示例进行了详细讲解,从宏观上讲明了OSG for Android项目的开发方法。这里给出链接:http://blog.csdn.net/dongzhong1990/article/details/51736868

  如果阅读过上一篇教程,也许会对一些细节上的问题还不是很清楚。例如,示例中osgNativeLib.cpp中那些奇怪命名方法的函数是怎样产生的,为什么要这样写。本篇教程将对该问题进行详细解答。

--------------------------------------------------

  众所周知,在开发Android项目时,我们通常使用的是Java语言。然而OSG却是一个基于C++平台的三维渲染引擎。于是,在这里就有了一个小小的冲突。那么,为了能够在Android平台上使用基于C++的OSG,我们应该怎么做呢?通常情况下,我们也许会产生以下两种想法:第一,将OSG重新用Java编写一遍;第二,通过某些方法,使Java可以调用C++;第三,算了,不用Java了。

  我们分别看看这三种方法。第一种方法,如果是对于一些小型的框架引擎,这也不失为一种办法。但是很多情况下,框架引擎代码量都是比较庞大的,如果对每个框架引擎都重新更换语言编写,那么工作量会十分巨大,而且很容易出现错误。第二种方法,简单可行高效,通过这种方法可以快速地将不同语言编写的框架引擎融合到同一个项目中,同时这种方法也是现阶段对不同平台融合的首选方法。第三种方法,算了,不考虑。

  那么,现在我们所要考虑的就是如何使Java与C++之间进行沟通。正如前几篇教程所提到的那样,在Java程序中使用C/C++需要使用JNI,即Java Native Interface。而Google公司又为Android开发提供了NDK,即Native Development Kit,作为Android项目中使用C/C++的工具集。本篇教程将对JNI和NDK的使用方式进行详细讲解,使学习者能够更加深入的理解Android调用OSG函数的内部机制。

  本文所用的NDK为r10d版本,下载链接:http://developer.android.com/tools/sdk/ndk/index.html。如果链接不上,可以百度一下,会有不少网盘中分享安装包。

  与上篇教程一样,本文默认项目已经配置完毕。具体配置方式,参考本系列教程(二),链接:http://blog.csdn.net/dongzhong1990/article/details/51736868。

  文章将分别从Java层和C/C++层进行阐述。讲解需要的代码都保存在了CODE平台上,链接:https://code.csdn.net/dongzhong1990/osgandroidhelloworld/tree/master

一、Java层相关实现

  在Android中调用OSG的C/C++函数,与普通的Android项目的区别并不是很大。在Java层中所需要做出的修改都集中在了osgNativeLib.java这个文件中。代码如下:

osgNativeLib.java:

package osg.dong.osghelloworld;public class osgNativeLib
{static{System.loadLibrary("osgNativeLib");}public static native void init(int width, int height);public static native void step();
}

  从代码中可以看到,osgNativeLib类中定义了两个带有native修饰符的方法,分别是init和step。这里的native修饰符就是用于指定该方法调用了本地语言的方法,即C/C++语言。也就是说,只要是在定义方法前加了native修饰符,那就是可以与C/C++层沟通的方法。而在Java层的其他地方,调用这些native方法,和调用普通的Java方法是没有任何区别的。

  同时我们也看到,在Java层中,并没有native方法的具体实现代码,仅仅只有一个声明。这是因为这些方法的具体实现都是在C/C++层中完成的。也就是说,这些方法的实际功能是在C/C++层上的,而在Java层上,这些方法仅仅是作为一个调用接口使用的。

  在osgNativeLib中,除了两个native方法外,还存在一个static静态块。在这个静态块中,系统载入了一个函数库,这个函数库的名称为“osgNativeLib”,如果阅读过本系列教程之前两篇的话,应该就知道,这个osgNativeLib的名字是在Android.mk配置文件中定义的。那么,这个库到底是怎么形成的呢,这里暂时先放一下,留个疑问,下文我会详细讲解。

  在Android项目中调用OSG函数,在Java层的实现就是这么简简单单的几句代码而已。真正的核心所在是在C/C++层和两层之间的沟通上。

二、C/C++层相关实现

  在上一篇教程中,我们遗留了一个疑问,那就是osgNativeLib.cpp文件中,有两个命名方式以、修饰符和参数类型都很奇怪的函数,这两个函数是怎么得来的?在这里我进行详细解答。

  其实,osgNativeLib.cpp里面的函数是可以通过jni的javah命令自动生成的。我们在编写好osgNativeLib.java中的native方法后,先build一下项目,这时候基本上会报错。我们不用管错误,build项目的目的是为了得到osgNativeLib.class文件,这个文件就是osgNativeLib.java经过编译后得到的class文件。Android项目中所有的class文件都会保存在<项目根目录>/bin/classes路径下。

  打开cmd,进入到<项目根目录>/bin/classes路径下,输入命令:javah osg.dong.osghelloworld.osgNativeLib。注意!这里需要将osgNativeLib类所在的包全名写上。之后,你就会在classes文件夹下发现一个文件名为osg_dong_osghelloworld_osgNativeLib.h的C/C++头文件。该文件代码如下:

osg_dong_osghelloworld_osgNativeLib.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class osg_dong_osghelloworld_osgNativeLib */#ifndef _Included_osg_dong_osghelloworld_osgNativeLib
#define _Included_osg_dong_osghelloworld_osgNativeLib
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     osg_dong_osghelloworld_osgNativeLib* Method:    init* Signature: (II)V*/
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_init(JNIEnv *, jclass, jint, jint);/** Class:     osg_dong_osghelloworld_osgNativeLib* Method:    step* Signature: ()V*/
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_step(JNIEnv *, jclass);#ifdef __cplusplus
}
#endif
#endif

  其中,我们看到了熟悉的身影。就是下面这两个函数声明:

JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_init(JNIEnv *, jclass, jint, jint);
JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_step(JNIEnv *, jclass);

  这两个函数声明就是我们所需要的。这两个函数分别对应了osgNativeLib.java中init方法和step方法。其命名规则是:Java_ + <包全名(“.”替换为“_”)> + _<函数名>。并在所有原有参数之前增加两个参数:JNIEnv*和jclass。其他的原有参数类型,因为是从Java转换到C/C++,所以会有一定的改变。这里给出Java、JNI和C/C++参数类型的对应规则:

  

  其中基本类型的变量可以直接转换。但是对象类型的转换相比而言就稍微麻烦些,具体的方法已经有很多人做了实现,在此我就不再赘述。

  好了,现在我们就知道了Java层中的native方法在C/C++层中的声明形式。现在我们就需要对这些声明了的函数进行定义。新建一个osgNativeLib.cpp文件,将下面代码复制进去:

extern "C" {JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_init(JNIEnv *, jclass, jint, jint);JNIEXPORT void JNICALL Java_osg_dong_osghelloworld_osgNativeLib_step(JNIEnv *, jclass);}

  同时,也不要忘了添加头文件,#include "jni.h"。而osg_dong_osghelloworld_osgNativeLib.h这个文件我们就可以删除了。记住,我们需要的只是其中的内容,而不是这个文件。

  剩下的事情就和标准的C/C++没什么区别了。在定义函数的时候,仍然需要加上JNIEXPORT和JNICALL,表示这是JNI调用的函数。

  接下来就可以尽情的“享用”OSG啦。

三、NDK的作用

  假设我们已经将OSG for Android项目的OSG部分实现完成,编写了好多的c、cpp、h文件。那么,我们现在可以运行项目了吗?稍安勿躁,我们还缺了一步。Android还是不能直接认识c、cpp和h的,我们需要将他们转换为Android可以认识的东西,那怎么办呢?这时候就需要NDK出场了。NDK的作用就是将这些C/C++文件编译成为Android可以认识的.so(动态库)和.a(静态库),至于到底是生成了哪种,则是在配置文件Android.mk文件末尾处的那句include $(BUILD_SHARED_LIBRARY)或者include $(LOCAL_STATIC_LIBRARY)决定的。同时,也别忘记了要把所有的.c文件和.cpp文件都要加到LOCAL_SRC_FILES里去,不少新手会容易犯这个错误,甚至是熟练开发人员也会偶尔遗忘这一点,要注意。

  对于我们来说,这时候需要做的就是build项目。NDK会自动地根据Android.mk文件的配置对C/C++文件进行编译,无需我们额外做的更多。生成的.so文件、.a文件可以在项目路径下的obj文件夹中找到,文件名是lib+<库名>+.so/.a。例如,本文示例生成的就是libosgNativeLib.so。而在osgNativeLib.java文件中静态块内载入的库就是这个。

--------------------------------------------------------

  

  本篇教程主要讲解了OSG for Android项目中使用到的JNI和NDK用法。在下一篇教程中,我将对OSG的基本常识进行讲解,以帮助OSG“纯新人”更好的理解OSG for Android项目,敬请关注。

OSG for Android新手教程系列(四)——JNI与NDK的使用相关推荐

  1. OSG for Android新手教程系列(三)——HelloWorld,第一个示例

    在上一篇教程中,我对OSG for Android的项目配置进行了讲解.在本篇教程中,我将通过一个最简单的示例,来讲解如何在Android项目中使用OSG.网上几乎所有的第一个示例,用的都是OSG库中 ...

  2. WPF入门教程系列四——Dispatcher介绍

    WPF入门教程系列四--Dispatcher介绍 一.Dispatcher介绍 微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢? 不管是WinForm应用程序还 ...

  3. 摄像头 保存到外网服务器_【小喵科技】物联网教程系列四:喵家外网IOT服务器...

    HOT新品热卖中■■■■■ 双向海量教学课程 无论是家长还是老师,都可以给孩子一个更好的未来 分享给更多的人 加入我们吧! 夏至の时光 ▼往期精彩课程在文章末尾 ▼喵家外网IOT服务器--快速上手 在 ...

  4. Android面试专题系列(四):Activity之间如何进行通信→LiveDataBus

    不诗意的女程序媛不是好厨师~ 转载请注明出处,From李诗雨-https://blog.csdn.net/cjm2484836553/article/details/105147592 <And ...

  5. Android开发教程--第一个JNI程序

    先配置NDK环境,网上教程很多.安装cygwin,此过程省略,如有不懂的地方可以百度. NDK路径D:\cygwin\home\Administrator 编辑.bash_profile这个文件 添加 ...

  6. 【转】Android 驱动开发系列四

    原文网址:http://www.2cto.com/kf/201304/202040.html 时隔多日,终于都抽出时间来写blog了.废话不多说,接着上一篇,这里将介绍如何编写HAL层(硬件抽象层)对 ...

  7. Android 系统开发系列四

    这里将介绍如何编写HAL层(硬件抽象层)对应的JNI方法. 1.定义JNI层接口 进入到android-4.0.4_r1.2/hardware/libhardware/include/hardware ...

  8. Quantopian教程系列四

    Writing a Contest Algorithm 本教程将: 给一个概述的quant竞赛和它是如何工作的(第一课). 浏览竞赛算法所需的每个标准(课程2-10). 就如何编写或修改算法以满足竞赛 ...

  9. Android学习笔记系列四2 —— Activity的生命周期

    2019独角兽企业重金招聘Python工程师标准>>> 启动一个Activity 使用startActivity(Intent intent). intent指定了你想要启动的act ...

最新文章

  1. Pycharm去掉项目所有 # 注释
  2. 在使用Reference Source调试.Net 源代码时如何取消optimizations(代码优化)-翻译
  3. Dataset之WebVision:WebVision数据集简介、下载、使用方法之详细攻略
  4. oracle批次处理数据_Oracle大批量删除数据方法(转)
  5. 【数据结构】绪论部分
  6. 用500行纯前端代码在浏览器中构建一个Tableau
  7. 使用 ADO.NET 的 NextResult 方法取得多个 Result Set
  8. 电脑刷机重装系统_手机刷机,原来也没有那么复杂
  9. Ubuntu解决包依赖关系
  10. Spring Boot 项目工程模板
  11. 凸优化第四章凸优化问题 4.3线性规划问题
  12. 对西方国家的智能制造研究综述:过去现在和未来
  13. (批处理)如何通过Python或批处理指令删除指定文件夹?
  14. 配置DTcms伪静态功能图文教程
  15. java枚举转换_java枚举类(转)
  16. Cloudcompare2.12.2使用vs2022带插件编译以及在WSL中编译cloudCompare【最新实践】
  17. C++ auto类型说明符如for(atuo x : s)
  18. 人生如逆旅,我亦是行人
  19. 《MySQL实战45讲》——学习笔记31 “误删数据的解决方案(删行/删表/删库/删实例)“
  20. trove mysql 镜像_OpenStack(Queens)制作 Trove 镜像

热门文章

  1. win10定期更新时间脚本
  2. 一个老油条面试的忠告
  3. c语言L9(34)正交实验程序,L9(34)正交表
  4. win10_64位+GTX1070max-q安装CUDA10.0.130+CUDNN7.4.2+Anaconda3.5.2+Tensorflow-gpu2.0.0+PyTorch-gpu1.2.0
  5. 国内大神成功给手机装上了 Win11,代码已在 GitHub 开源!
  6. Ubuntu remmina高级用法-使用ssh隧道连接xrdp
  7. 职业生涯规划之Java学习路线
  8. 华北工控专用计算机方案,餐饮业智能化转型,华北工控可提供餐饮机器人专用计算机产品方案...
  9. SNP2HLA之参考数据集合并提高分型准确性
  10. 用c++做一个简单的打飞机小游戏(详细说明与注释)