Android NDK开发和JNI开发.docx
- 文档编号:10494295
- 上传时间:2023-02-14
- 格式:DOCX
- 页数:32
- 大小:119.79KB
Android NDK开发和JNI开发.docx
《Android NDK开发和JNI开发.docx》由会员分享,可在线阅读,更多相关《Android NDK开发和JNI开发.docx(32页珍藏版)》请在冰豆网上搜索。
AndroidNDK开发和开发和JNI开发开发第九章第九章NDK开发与开发与JNI开发开发9.1NDK开发开发我们先来说说AndroidSDK(AndroidSoftwareDevelopmentKit),即Android软件开发工具包。
可以说只要你使用Java去开发Android这个东西就必须用到。
他包含了SDKManager和AVDManage。
对于android系统的一些开发版本的管理以及模拟器管理。
而NDK(NativeDevelopmentKit)跟SDK差不多的是他也是一个开发工具包。
用他开发C/C+是很方便的。
他有一个强大的编译集合。
9.1.1NDK产生的背景Android平台从诞生起,就已经支持C、C+开发。
众所周知,Android的SDK基于Java实现,这意味着基于AndroidSDK进行开发的第三方应用都必须使用Java语言。
但这并不等同于“第三方应用只能使用Java”。
在AndroidSDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库,即在Android平台上,“Java+C”的编程方式是一直都可以实现的。
不过,Google也表示,使用原生SDK编程相比Dalvik虚拟机也有一些劣势,AndroidSDK文档里,找不到任何JNI方面的帮助。
即使第三方应用开发者使用JNI完成了自己的C动态链接库(so)开发,但是so如何和应用程序一起打包成apk并发布?
这里面也存在技术障碍。
比如程序更加复杂,兼容性难以保障,无法访问FrameworkAPI,Debug难度更大等。
开发者需要自行斟酌使用。
于是NDK就应运而生了。
NDK全称是NativeDevelopmentKit。
NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。
NDK将是Android平台支持C开发的开端。
9.1.2为什么使用NDK1、代码的保护。
由于apk的Java层代码很容易被反编译,而C/C+库反汇难度较大。
2、可以方便地使用现存的开源库。
大部分现存的开源库都是用C/C+代码编写的。
3、提高程序的执行效率。
将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
4、便于移植。
用C/C+写得库可以方便在其他的嵌入式平台上再次使用。
9.1.3NDK简介1、NDK是一系列工具的集合NDK提供了一系列的工具,帮助开发者快速开发C/C+的动态库,并能自动将so和java应用一起打包成apk。
这些工具对开发者的帮助是巨大的。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
2、NDK提供了一份稳定、功能有限的API头文件声明Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。
从该版本的NDK中看出,这些API支持的功能非常有限,包含有:
C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。
在深入理解之前,暂且就把NDK当成是一种工具,这种工具使得JAVA能够使用C/C+编译出的so包。
并将此包一起打入apk包中。
9.1.4NDK开发环境的搭建1、下载安装AndroidNDK下载地址:
http:
/如图9-1所示图9-12、下载安装cygwin由于NDK编译代码时必须要用到make和gcc,所以你必须先搭建一个linux环境,cygwin是一个在windows平台上运行的unix模拟环境,它对于学习unix/linux操作环境,或者从unix到windows的应用程序移植,非常有用。
通过它,你就可以在不安装linux的情况下使用NDK来编译C、C+代码了。
下载地址:
http:
/下载,也可以到中文的映像网站http:
/下载1)然后双击运行吧,运行后你将看到安装向导界面。
如图9-2所示图9-22)点击下一步,此时让你选择安装方式:
InstallfromInternet:
直接从Internet上下载并立即安装(安装完成后,下载好的安装文件并不会被删除,而是仍然被保留,以便下次再安装)。
DownloadWithoutInstalling:
只是将安装文件下载到本地,但暂时不安装。
InstallfromLocalDirectory:
不下载安装文件,直接从本地某个含有安装文件的目录进行安装。
3)选择第一项,然后点击下一步。
4)选择要安装的目录,注意,最好不要放到有中文和空格的目录里,似乎会造成安装出问题,其它选项不用变,之后点下一步:
5)上一步是选择安装cygwin的目录,这个是选择你下载的安装包所在的目录,默认是你运行setup.exe的目录,直接点下一步就可以:
6)此时你共有三种连接方式选择:
DirectConnection:
直接连接。
UseIE5Settings:
使用IE的连接参数设置进行连接。
UseHTTP/FTPProxy:
使用HTTP或FTP代理服务器进行连接(需要输入服务器地址、端口号)。
用户可根据自己的网络连接的实情情况进行选择,一般正常情况下,均选择第一种,也就是直接连接方式。
然后再点击“下一步”。
7)这是选择要下载的站点,选择后点下一步。
8)此时会下载加载安装包列表9)Search是可以输入你要下载的包的名称,能够快速筛选出你要下载的包。
那四个单选按钮是选择下边树的样式,默认就行,不用动。
View默认是Category,建议改成full显示全部包再查,省的一些包被隐藏掉。
左下角那个复选框是是否隐藏过期包,默认打钩,不用管它就行,下边开始下载我们要安装的包吧,为了避免全部下载,这里列出了后面开发NDK用得着的包:
autoconf2.1、automake1.10、binutils、gcc-core、gcc-g+、gcc4-core、gcc4-g+、gdb、pcre、pcre-devel、gawk、make共12个包10)然后开始选择安装这些包吧,点skip,把它变成数字版本格式,要确保Bin项变成叉号,而Src项是源码,这个就没必要选了。
11)下面测试一下cygwin是不是已经安装好了。
运行cygwin,在弹出的命令行窗口输入:
cygcheck-ccygwin命令,会打印出当前cygwin的版本和运行状态,如果status是ok的话,则cygwin运行正常。
然后依次输入gccversion,g+-version,makeversion,gdbversion进行测试,如果都打印出版本信息和一些描述信息,则cygwin安装成功!
3、配置NDK环境变量1)首先找到cygwin的安装目录,找到一个home.bash_profile文件,我的是:
E:
cygwinhomeAdministrator.bash_profile,(注意:
我安装的时候我的home文件夹下面什么都没有,解决的办法:
首先打开环境变量,把里面的用户变量中的HOME变量删掉,在E:
cygwinhome文件夹下建立名为Administrator的文件夹(是用户名),然后把E:
cygwinetcskel.bash_profile拷贝到该文件夹下)。
2)打开bash_profile文件,添加NDK=/cygdrive/例如:
NDK=/cygdrive/e/android-ndk-r5exportNDKNDK这个名字是随便取的,为了方面以后使用方便,选个简短的名字,然后保存3)打开cygwin,输入cd$NDK,如果输出上面配置的/cygdrive/e/android-ndk-r5信息,则表明环境变量设置成功了。
9.1.5初试NDK开发在进行NDK开发时,一般需要同时建立Android工程和C/C+工程,然后使用NDK编译C/C+工程,形成可以被调用的共享库,最后共享库文件会被拷贝到Android工程中,并被直接打包到apk文件中1、编写Java代码1)建立一个Android工程TestNDK,创建TestNDK.java文件。
TestNDK.java文件内容如下:
packagecom.blueeagle.example;importandroid.app.Activity;importandroid.widget.TextView;importandroid.os.Bundle;publicclassTestNDKextendsActivityOverridepublicvoidonCreate(BundlesavedInstanceState)super.onCreate(savedInstanceState);TextViewmyTextView=newTextView(this);myTextView.setText(stringTestNdk();setContentView(myTextView);publicnativeStringstringTestNdk();publicnativeStringstringTestNdk2();staticSystem.loadLibrary(testNDK);其中static语句块表明程序开始运行的时候会加载testNDK,static区声明的代码会先于onCreate方法执行。
如果程序中有多个类,而且如果TestNDK这个类不是你应用程序的入口,那么testNDK(完整的名字是libtestNDK.so)这个库会在第一次使用TestNDK这个类的时候加载。
publicnativeStringstringTestNdk();publicnativeStringstringTestNdk2();可以看到这两个方法的声明中有native关键字,这个关键字表示这两个方法是本地方法,也就是说这两个方法是通过本地代码(C/C+)实现的,在java代码中仅仅是声明。
用eclipse编译该工程,生成相应的.class文件,这步必须在下一步之前完成,因为生成.h文件需要用到相应的.class文件。
暂时不考虑报错信息。
2)生成.h文件在C/C+文件编写之前,需要利用javah这个工具生成相应的.h文件,然后根据这个.h文件编写相应的C/C+代码。
进入到刚才建立的testNDK工程目录中,查看工程文件:
AndroidManifest.xmlassetsbindefault.propertiesgenressrc并新建一个NDK的文件夹后就可以进行.h文件的生成了。
在工程目录下执行:
javah-classpathbin-dndkcom.blueeagle.example.TestNDK。
这里-classpath表示类的路径;-dndk表示生成的.h文件存放的目录;com.blueeagle.example.TestNDK则是完整的类名。
现在可以再ndk目录下看到多了一个.h文件:
com_blueeagle_example_testNDK.h;打开后,可以看到.h的内容:
#include#ifndef_Included_com_blueeagle_example_testNDK#define_Included_com_blueeagle_example_testNDK#ifdef_cplusplusexternC#endif/*Class:
com_blueeagle_example_testNDK*Method:
stringTestNdk*Signature:
()Ljava/lang/String;*/JNIEXPORTjstringJNICALLJava_com_blueeagle_example_testNDK_stringTestNdk(JNIEnv*,jobject);/*Class:
com_blueeagle_example_testNDK*Method:
stringTestNdk2*Signature:
()Ljava/lang/String;*/JNIEXPORTjstringJNICALLJava_com_blueeagle_example_testNDK_stringTestNdk2(JNIEnv*,jobject);#ifdef_cplusplus#endif#endif上面代码中的JNIEXPORT和JNICALL是jni的宏,在android的jni中不需要,当然写上去也不会有错。
函数名比较长但是完全按照:
java_pacakege_class_mathod形式来命名。
也就是说:
TestNDK.java中stringTestNdk()方法对应于C/C+中的Java_com_blueeagle_example_testNDK_stringTestNdk()方法TestNDK.java中stringTestNdk2()方法对应于C/C+中的Java_com_blueeagle_example_testNDK_stringTestNdk2()方法。
注意下其中的注释:
Signature:
()Ljava/lang/String;()Ljava/lang/String;()表示函数的参数为空(这里为空是指除了JNIEnv*,jobject这两个参数之外没有其他参数,JNIEnv*,jobject是所有jni函数必有的两个参数,分别表示jni环境和对应的java类(或对象)本身),Ljava/lang/String;表示函数的返回值是java的String对象。
2、编写C/C+文件TestNDK.c#include#includejstringJava_com_blueeagle_example_testNDK_stringTestNdk(JNIEnv*env,jobjectthiz)return(*env)-NewStringUTF(env,HelloTestNDK!
);这里只是实现了Java_com_blueeagle_example_testNDK_stringTestNdk方法,而Java_com_blueeagle_example_testNDK_stringTestNdk2方法并没有实现,因为在testNDK.java中只调用了stringTestNdk()方法,所以stringTestNdk2()方法没有实现也没关系,不过建议最好还是把所有java中定义的本地方法都实现了。
Java_com_blueeagle_example_testNDK_stringTestNdk函数只是简单的返回了一个内容为HelloTestNDK!
的jstring对象(对应于java中的String对象)。
testNDK.c文件就已经编写好了,这时的.h文件已经没有用了。
3、编译生成相对应的库1)首先需要编写Android.mk文件在testNDK.c的同级目录下新建一个Android.mk的文件LOCAL_PATH:
=$(callmy-dir)include$(CLEAR_VARS)LOCAL_MODULE:
=testNDKLOCAL_SRC_FILES:
=testNDK.cinclude$(BUILD_SHARED_LIBRARY)这个Androd.mk文件很短,下面我们来逐行解释下:
LOCAL_PATH:
=$(callmy-dir)一个Android.mk文件首先必须定义好LOCAL_PATH变量。
它用于在开发树中查找源文件。
在这个例子中,宏函数my-dir,由编译系统提供,用于返回当前路径(即包含Android.mkfile文件的目录)。
include$(CLEAR_VARS)CLEAR_VARS由编译系统提供,指定让GNUMAKEFILE为你清除许多LOCAL_XXX变量(例如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES,等等.),除LOCAL_PATH。
这是必要的,因为所有的编译控制文件都在同一个GNUMAKE执行环境中,所有的变量都是全局的。
LOCAL_MODULE:
=testNDK编译的目标对象,LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。
名称必须是唯一的,而且不包含任何空格。
注意:
编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为hello-jni的共享库模块,将会生成libhello-jni.so文件。
重要注意事项:
如果你把库命名为libtestNDK,编译系统将不会添加任何的lib前缀,也会生成libfoo.so,这是为了支持来源于Android平台的源代码的Android.mk文件,如果你确实需要这么做的话。
LOCAL_SRC_FILES:
=testNDK.c,LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C+源代码文件。
注意,你不用在这里列出头文件和包含文件,因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。
默认的C+源码文件的扩展名是.cpp.指定一个不同的扩展名也是可能的,只要定义LOCAL_DEFAULT_CPP_EXTENSION变量,不要忘记开始的小圆点(也就是.cxx,而不是cxx)include$(BUILD_SHARED_LIBRARY)BUILD_SHARED_LIBRARY表示编译生成共享库,是编译系统提供的变量,指向一个GNUMakefile脚本,负责收集自从上次调用include$(CLEAR_VARS)以来,定义在LOCAL_XXX变量中的所有信息,并且决定编译什么,如何正确地去做。
还有BUILD_STATIC_LIBRARY变量表示生成静态库:
lib$(LOCAL_MODULE).a,BUILD_EXECUTABLE表示生成可执行文件。
2)其次进行编译进入到工程根目录,输入ndk-build即可生成相应的库libs/armeabi/testNDK.so9.1.6生成APK重新编译testNDK工程,将so包打入工程apk包,即可看到结果。
在模拟器上显示HelloTestNDK!
9.2JNI开发原理开发原理JNI是Java平台中的一个强大特性。
应用程序可以通过JNI把C/C+代码集成进Java程序中。
通过JNI,开发者在利用Java平台强大功能的同时,又不必放弃对原有代码的投资;因为JNI是Java平台定义的规范接口,当程序员向Java代码集成本地库时,只要在一个平台中解决了语言互操作问题,就可以把该解决方案比较容易的移植到其他Java平台中。
Java平台(JavaPlatform)的组成:
JavaVM和JavaAPI.Java应用程序使用Java语言开发,然后编译成与平台无关的字节码(.class文件)。
JavaAPI由一组预定义的类组成。
任何组织实现的Java平台都要支持:
Java编程语言,虚拟机,和API平台环境:
操作系统,一组本机库,和CPU指令集。
本地应用程序,通常依赖于一个特定的平台环境,用C、C+等语言开发,并被编译成平台相关的二进制指令,目标二进制代码在不同OS间一般不具有可移植性。
Java平台(JavaVM和JavaAPI)一般在某个平台下开发。
比如,Sun的JavaRuntimeEnvironment(JRE)支持类Unix和Windows平台.Java平台做的所有努力,都为了使程序更具可移植性。
9.2.1JNI的作用当Java平台部署到本地系统中,有必要做到让Java程序与本地代码协同工作。
部分是由于遗留代码(保护原有的投资)的问题(一些效率敏感的代码用C实现,但现在JavaVM的执行效率完全可信赖),工程师们很早就开始以C/C+为基础构建Java应用,所以,C/C+代码将长时间的与Java应用共存。
JNI让你在利用强大Java平台的同时,使你仍然可以用其他语言写程序。
作为JavaVM的一部分,JNI是一套双向的接口,允许Java与本地代码间的互操作。
如图9-3所示图9-3作为双向接口,JNI支持两种类型本地代码:
本地库和本地应用。
用本地代码实现Java中定义的nativemethod接口,使Java调用本地代码通过JNI你可以把JavaVM嵌到一个应用程序中,此时Java平台作为应用程序的增强,使其可以调用Java类库比如,在浏览器中运行Applet,当浏览器遇到Applet标签,浏览器就会把标签中的内容交给JavaVM解释执行,这个实现,就是典型的把JavaVM嵌入Browser中。
9.2.2什么时候使用JNI当你准备在项目中使用JNI之前,请先考虑一下是否有其他更合适的方案。
上节有关JNI缺点的介绍,应该引起你足够的重视。
这里介绍几个不通过JNI与其他语言交互的技术:
IPC或者通过TCP/IP网络方案(AndroidASE)数据库方面,可以使用JDBC使用Java的分布式对象技术:
JavaIDLAPI注:
IPC与TCP/IP是常用的基于协议的信息交换方案.可以参考Android上的Binder和ASE(AndroidScriptEnvironment)。
一典型的解决方案是,Java程序与本地代码分别运行在不同的进程中.采用进程分置最大的好处是:
一个进程的崩溃,不会立即影响到另一个进程。
但是,把Java代码与本地代码置于一个进程有时是必要的。
如下:
JavaAPI可能不支某些平台相关的功能。
比如,应用程序执行中要使用JavaAPI不支持的文件类型,而如果使用跨进程操作方式,即繁琐又低效避免进程间低效的数据拷贝操作多进程的派生:
耗时、耗资源(内存)用本地代码或汇编代码重写Java中低效方法总之,如果Java必须与驻留同进程的本地代码交互,请使用JNI。
9.2.2JNI的发展关于Java应用程序如何与本地代码互操作的问题,在Java平台早期就被提了出来JDK1.0包括了一套与本地代码交互的接口。
当时许多Java方法和库都依赖本地方法实现(如java.io,)。
但是,JDKrelease1.0有两个主要问题:
Java虚拟机规范未定义对象布局,本地代码访问对象的成员是通过访问C结构的成员实现的本地代码可以得到对象在内存中的地址,所以,本地方法是GC相关的为解决上述问题对JNI做了重新设计,让这套接口在所有平台都容易得到支持。
虚拟机实现者通过JNI支持大量的本地代码工具开发商不用处理不同种类的本地接口所有JNI开发者面对的是操作JavaVM的规范APIJNI的首次支持是在JDKrelease1.1,但1.1内部Java与本地代码的交互仍然使用原始方式(JDK1.0).但这种局面,没有持续很久,在Java2S
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android NDK开发和JNI开发 NDK 开发 JNI