赞
踩
写于2014年那个办公室停电导致热爆了汗流浃背的夏天。
NDK是由谷歌娘提供的,某种意义上就是可以让android使用c开发的第“三”方sdk,所以,正常来说eclipse是没有配置这个东西的,当然如我所云,我只考虑用最小的工程成本(较少的时间保证一定质量)来实现我的目标,所有我使用的是由谷歌提供的标准的ADT,下载完安装后可以自动完成android开发的基本配置,也就是可以直接拿来写HelloAndroid的开发工具,当然,普通的android开发也不是我这里讨论的问题,所以以下涉及到java下完成android开发的问题我默认所有人都能理解我说的东西,不会做解释,我的解释会放在如何使用NDK上(啰嗦的我啊愿萌萌的油乎乎永安你的灵魂,普天齐明!我受到攻击hp-250,我触发被动天赋厚脸皮hp+2500)。
另外由于我配置完也过了有大半个月了,所以有些细节可能不会记得很清楚,也可能会遗漏一些细节,如果我日后还活着而且想起来了,我发四一定会回来修改补充这篇博客。或者在配置过程中遇到有什么问题可以留言补充,有缘的话我一定会看到的。
Android严格来说是linux的一个分支,换个角度讲,android就是一个被修改过的linux系统,linux系统运行直接用c写的东西当然是没问题的,这也是为什么android可以允许ndk的存在的一个核心原因。而偶们平时使用的c开发版本里边,大方向上可能可以算作有两种,一种是GUNC,一种是个windows用的C(原谅无知的我又忘了叫什么了,要不简称wc吧),这名字不是太重点,重点是这两种c虽然大同小异,但是却会有严重的跨平台问题,gunc是个unix系统使用为主,也就是在unix环境下编译gunc是个事半功倍的事情,wc的问题同。linux是unix的分支,所以linux下也是使用gunc的,换个角度说,我这段想表达的问题很简单,windows下做NDK会多很多麻烦,根据我的原则以及我的开发环境考虑,我勇敢的放弃了在windows下的努力(显然我是被折腾的不行了才放弃的,做项目跟做研究毕竟有差别,项目工程有工期的鸭梨,使用一个更稳定更可控的平台是更好的选择),我使用的开发环境是unix的另外一个分支,mac,也就是苹果的操作系统(显然全名太长了我有限的脑容量是记不住的,打个mac意思一下咯)。换个角度说,android和mac都是属于unix系统的大类中,就像香港人和深圳人要交流总比深圳人跟加州人要交流容易些一样,mac下做android的工作也比在windows下做要容易一些(说白了就是给android做windows开发工具的人比较少——)。换个角度,linux下做ndk也会更容易些,可能某些细节不完全一样,但大体应该是接近的。
嗯嗯,上一章其实没什么好看的,可以跳过,这章开始正式讨论使用ndk过程中遇到(或者说需要解决的问题)。
宏观上偶们先梳理一下逻辑,整个过程中偶们需要解决哪些问题,后续章节会逐个讲述这些问题的具体解决方案(有些方案并不唯一,有兴趣可以自己再研究)。
调用ndk首先要解决的就是ndk在哪的问题?ndk不是凭空出现的,可以到google提供ndk的地方去下载,由于网络的原因可能有些人没法下载到,可以百度到一些第三方的地方下载,注意使用的操作系统是mac,64位,版本肯定要最新的啦,旧的版本可能会出现有些功能不支持的问题。
下载好ndk以后当然是要让开发工具,也就是eclipse君知道有这个东西已经存在在电脑深深的脑海里,TA的硬盘里,TA的内存里,TA的废纸篓里。
eclipse知道怎么从垃圾箱里找出ndk以后,就需要配置ndk开发环境了,android调用ndk的方式一般是通过调用动态链接库的方式完成的,所以所谓配置ndk开发设置,实质上是为了配置编译器如何编译写好的c代码。说真的这块很多麻烦事,我当时是被弄得很崩溃。
显然java无法那么简单就可以调用c的函数,所以需要使用ndk提供的中间接口,这个接口一式两份,一份是用java编写的,另一份使用C编写。这两份协议内容实质是一样的,虽然两者用的语言不一样,就像一些国际商业合同可能会一份是中文,一份是英文一样。
ndk提供的与标准c类似jint、jstring等“c的数据类型”(就是说,这些数据类型虽然姓“j”,但它还是一个c的数据类型。就像金刚石没有金属,也跟某只猩猩没有关系;铅笔也不是铅做的一样。“j”只是描述了这个数据类型具有某些特殊性质),通过这些数据类型可以通过ndk与java中的对应int类型、string类型实现自由转换。
由于动态链接库的后缀是*.so,所以下文统称so文件(体谅下我鼠标手键盘爪多年打字不易)。当你的代码写完以后,就需要开始编译了,编译有两种结果,All In or Nothing(成功或者失败=。=)。成功的话会生成一个so文件,这个so文件可以认为是由以下三个部分编译而成的(你写的代码,你调用的库,ndk的接口),虽然编译so文件并不需要ndk接口的java部分,但如果没有这个部分,这个so文件是无法正常使用的,就像一辆车,没有钥匙是没法开的(神马,你会撬锁,那确实可以用,就是成本略高)使用这个so文件配合ndk接口的java部分就可以形成一个完成的NDK动态链接库了。
到这一步就已经很接近成功了,把动态链接库加入已有的项目,留意之前准备好的ndk接口中的java部分,在正常的java函数中调用它吧,相信自己,你可以的。
调试神马的,就不多说了,大伙都懂的,使用NDK可以考虑配合TDD的开发模式,原理不多说,显然会跑题。
就像所说的第一个问题,先下载好ndk的开发包,解压缩,记下保存的路径,例如
“/Applications/adt-bundle-mac-x86_64-20140321/android-ndk-r9d/”。
显然,java的“编译器”是不认识C的东西滴,同理,C滴东西也不认识java是神马,所以偶们需要通过一些接口协议来完成这样一部分,也就是实现让java能调用C的函数,并正确地把java的对象传给C,同时C能正确的获得并解析Java传过来的对象,并正确地把结果转换为java的对象传回去。
java的对象可以看做两种,一种是如int、double、string这样的值对象,另一种是自己建立的继承自object的自定义对象(以下成为object对象)。事实上两种对象都可以传递给C进行处理,但显然后一种由于是自定义的会更复杂,作为进阶内容,我就不啰嗦了,这里就啰嗦一下普通的数值类型,以下以String类型在java与C的通讯为例,其他的数值传递问题可以举一反三,或者自己百度=。=
假设偶们现在要做的事情是输入一个字符串,然后返回这个字符串的长度,偶们首先实现一个java版的。新建一个叫做NdkForJava的类,package com.hellondk;类定义如下:
public class NdkForJava
{
public int SizeOfString(String str)
{
return str.length();
}
}
这个函数显然不是很难,但里边包含了这些关键点:首先,实现了String参数的传入;再者,对传入的String参数进行了计算;最后,返回了一个int参数。这几点已经可以完整的应付最基本的NDK调用问题了。
那这个函数的NDK协议应该怎么写呢?其实很简单,将上一个函数的主体部分删掉(也就是别写主体,光起名就好),再增加一个关键字native即可:
public native int SizeOfString(String str);
这里有几个点跟java的普通函数写法有些不同,首先,增加了关键字native,也就是NDK中的N,该关键字声明了这个函数的实现交由NDK完成,然后,省略了函数主体,因为函数主体是用C完成的嘛,要在这写了就能用,还要java干嘛=。=
好,就酱,java部分的通讯协议就酱就可以了。
先回到之前创建的“IHateNDK.cpp”中,添加第一行语句,添加一个NDK专用的头文件jni.h,里边包含了java与c实现通讯的核心函数及对象,要完成通讯,这个部分是必须有的,也是ndk专用的一个,标准C(显然我介里说的是GUN-C)没有的头文件。
完成了include以后,根据java协议的情况,声明一个函数如下:
extern "C"
{
JNIEXPORT jint Java_com_hellondk_NdkForJava_SizeOfString(JNIEnv* env, jobject thiz, jstring str);
}
这里有很多关键点:
显然这个函数名隐含天地大道,好像又符合某种天地之理,怎么看和都好像信息量很大的样子,似乎跟java的协议有某种关联(呸,瞎子都看得出来啦,再啰嗦板砖伺候;啊,这要靠领悟的喂)。
C协议中的函数名为了跟java协议中的函数名对应,所以命名的格式(区分大小写)为Java_com_包名1_包名2_……包名n类名_函数名。
NDK使用了“反射”的技术完成了通过java调用C函数的功能,所以这个名字非常重要,千万不能写错,写错的话编译器是无法正常加载NDK中的函数的。
extern "C"
{
//...
}
C++语言在编译的时候为了解决函数的多态问题,会将函
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。