当前位置:   article > 正文

Mac下使用Eclipse实现Android中调用C/C++(NDK)基础详细教程_eclipse c++ android

eclipse c++ android

写于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过程中遇到(或者说需要解决的问题)。

宏观上偶们先梳理一下逻辑,整个过程中偶们需要解决哪些问题,后续章节会逐个讲述这些问题的具体解决方案(有些方案并不唯一,有兴趣可以自己再研究)。

让开发工具Eclipse知道NDK已经存在。

调用ndk首先要解决的就是ndk在哪的问题?ndk不是凭空出现的,可以到google提供ndk的地方去下载,由于网络的原因可能有些人没法下载到,可以百度到一些第三方的地方下载,注意使用的操作系统是mac,64位,版本肯定要最新的啦,旧的版本可能会出现有些功能不支持的问题。

下载好ndk以后当然是要让开发工具,也就是eclipse君知道有这个东西已经存在在电脑深深的脑海里,TA的硬盘里,TA的内存里,TA的废纸篓里。

配置ndk的开发设置(参数)。

eclipse知道怎么从垃圾箱里找出ndk以后,就需要配置ndk开发环境了,android调用ndk的方式一般是通过调用动态链接库的方式完成的,所以所谓配置ndk开发设置,实质上是为了配置编译器如何编译写好的c代码。说真的这块很多麻烦事,我当时是被弄得很崩溃。

实现java和c通讯的接口(以下称ndk接口)。

显然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动态链接库了。

将动态链接库添加入已有的android项目中。

到这一步就已经很接近成功了,把动态链接库加入已有的项目,留意之前准备好的ndk接口中的java部分,在正常的java函数中调用它吧,相信自己,你可以的。

调试和……

调试神马的,就不多说了,大伙都懂的,使用NDK可以考虑配合TDD的开发模式,原理不多说,显然会跑题。

基本的配置

就像所说的第一个问题,先下载好ndk的开发包,解压缩,记下保存的路径,例如
“/Applications/adt-bundle-mac-x86_64-20140321/android-ndk-r9d/”。

  1. 打开你的eclipse(也就是我这里的ADT),在ADT—Preferences-Android-NDK里边把这个路径设置一下,这样ADT就知道你的NDK在哪里了。
    这里写图片描述
    这里写图片描述
  2. 假设现在新建了一个Android的项目,或者已经有了一个Android的项目,“HelloNDK”。
    这里写图片描述
    这里写图片描述
    这里写图片描述
  3. 在项目上点击右键,选择Android Tools—Add Native Support…
  4. 设置将创建的第一个so文件的名字,所有的so文件都是而且必须以lib***.so的格式命名,这样APK在运行的时候才能正确加载,点击Finish。
    这里写图片描述
  5. 这个时候你会发现你的Project里边多出了一个jni的文件夹,木有错,介个文件夹就是给你放*.c*.cpp*.h这些文件的地方啦。里边默认生成了两个文件,一个”IHateNDK.cpp”,一个“Android.mk”。注意,第一个文件不是必须的,里边什么都没有,可以自己另外创建,第二个文件才是必须存在的,关于cpp、mk文件的说明,后边会有进一步的介绍,本章只讨论配置的过程。
    这里写图片描述
    这里写图片描述
    这里写图片描述
  6. 点击Project—Build All,这样就能完成对文件的编译了
    这里写图片描述
  7. Build的结果会输出在Console窗口里边
    这里写图片描述
  8. 编译成功的SO文件会保存在项目工程的libs\armeabi\目录下。
  9. 到这一步为止,编译一个新的NDK动态链接库的流程就走完了,换个角度讲,剩下的就是如何调用以及如何完成代码的部分了。

java与c++之间的通信

显然,java的“编译器”是不认识C的东西滴,同理,C滴东西也不认识java是神马,所以偶们需要通过一些接口协议来完成这样一部分,也就是实现让java能调用C的函数,并正确地把java的对象传给C,同时C能正确的获得并解析Java传过来的对象,并正确地把结果转换为java的对象传回去。

java的对象可以看做两种,一种是如int、double、string这样的值对象,另一种是自己建立的继承自object的自定义对象(以下成为object对象)。事实上两种对象都可以传递给C进行处理,但显然后一种由于是自定义的会更复杂,作为进阶内容,我就不啰嗦了,这里就啰嗦一下普通的数值类型,以下以String类型在java与C的通讯为例,其他的数值传递问题可以举一反三,或者自己百度=。=

协议的Java部分。

假设偶们现在要做的事情是输入一个字符串,然后返回这个字符串的长度,偶们首先实现一个java版的。新建一个叫做NdkForJava的类,package com.hellondk;类定义如下:

public class NdkForJava
{
   
    public int SizeOfString(String str)
    {
        return str.length();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个函数显然不是很难,但里边包含了这些关键点:首先,实现了String参数的传入;再者,对传入的String参数进行了计算;最后,返回了一个int参数。这几点已经可以完整的应付最基本的NDK调用问题了。

那这个函数的NDK协议应该怎么写呢?其实很简单,将上一个函数的主体部分删掉(也就是别写主体,光起名就好),再增加一个关键字native即可:

public native int SizeOfString(String str);
  • 1

这里有几个点跟java的普通函数写法有些不同,首先,增加了关键字native,也就是NDK中的N,该关键字声明了这个函数的实现交由NDK完成,然后,省略了函数主体,因为函数主体是用C完成的嘛,要在这写了就能用,还要java干嘛=。=

好,就酱,java部分的通讯协议就酱就可以了。

协议的C部分。

先回到之前创建的“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);
}
  • 1
  • 2
  • 3
  • 4

这里有很多关键点:

函数名

显然这个函数名隐含天地大道,好像又符合某种天地之理,怎么看和都好像信息量很大的样子,似乎跟java的协议有某种关联(呸,瞎子都看得出来啦,再啰嗦板砖伺候;啊,这要靠领悟的喂)。

C协议中的函数名为了跟java协议中的函数名对应,所以命名的格式(区分大小写)为Java_com_包名1_包名2_……包名n类名_函数名。

NDK使用了“反射”的技术完成了通过java调用C函数的功能,所以这个名字非常重要,千万不能写错,写错的话编译器是无法正常加载NDK中的函数的。

关键字extern

extern "C"
{
    //...
}
  • 1
  • 2
  • 3
  • 4

C++语言在编译的时候为了解决函数的多态问题,会将函

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/350808
推荐阅读
相关标签
  

闽ICP备14008679号