不少Android和iOS项目中,因为种种原因不得不调用C/C++代码。这篇文章主要讲述如何通过Objective-C++、NDK技术在iOS及Android设备上调用C/C++代码。
主要工作原理
主要工作原理,如上图所示。Google Android提供NDK以便支持C/C++代码,而iOS可以通过Objective-C++(*.mm)来支持C/C++代码的编译运行。如果有朋友钻研过Cocos2d-x的话,应该对上述这些内容非常熟悉。好了,废话不多说,开始提供干货。
需要调用的C++代码
我们先编写一个简单的C++代码,以便能够在Android和iOS设备上调用。为了简单起见,也为了能够充分展示操作过程,我们设计如下的C++类:
MyClz.h
文件
#include <iostream>
class MyClz {
public:
double getSum(const double num1, const double num2);
};
在上述MyClz.h
头文件中,我们引入了iostream
,并声明了一个类MyClz
,其中包含一个公开的方法getSum
,其作用很明显:传入两个double类型的数值,计算并返回两个数的和。
MyCLz.cpp
文件
#include "MyClz.h"
double MyClz::getSum(const double num1, const double num2){
return num1 + num2;
}
int main() {
return 0;
}
cpp文件中,我们实现了头文件MyClz.h
中声明的getSum
方法的内容,计算两个数的和并将其反馈给调用者。
接下来,我们来看看,如何在Android和iOS设备上调用上述MyClz
类中的getSum
方法。
在Android设备调用
- 下载并配置Android Studio
本例中,采用了最新版的Android Studio(v1.2.1.1)作为开发环境。下载并安装,同时根据自己的需要下载相应的Android SDK并配置好各项开发环境。请注意:如下网页需要「科学上网」才能正常访问
Android Studio:http://developer.android.com/sdk/index.html - 下载并配置NDK
NDK是Google提供的一套用以在Android设备上调用C/C++代码的工具集。某些情况下,可能没有办法使用Java语言完成的任务,只能借助底层的本地代码(native code)实现。这时,NDK就会派上用场。到如下地址下载NDK并将其解压到本地磁盘的某个路径即可。
请注意:如下页面需要「科学上网」才能正常访问
Android NDK:http://developer.android.com/tools/sdk/ndk/index.html - 新建一个Android项目
在Android Studio中新建一个Android项目。如果习惯了Eclipse+ADT的朋友,可能会对Android Studio感到无比蛋疼。但是,根据我的使用经验来看,Android Studio完全可以取代Eclipse+ADT成为今后的主流Android IDE。再者,这玩意儿既然是Google主推的产品,应该会越来越受到重视,所以早点习惯才好。
新建好的Android项目的大体目录结构如下图所示:
上述目录结构中,对开发者而言非常关键的几个文件及文件夹有:
{project_home}/app
Android项目的主体目录{project_home}/app/build.gradle
app构建配置文件{project_home}/app/src
项目源代码{project_home}/local.properties
SDK、NDK路径配置文件 - 修改
{project_home}/local.properties
文件
该文件种默认有一行,是指定Android SDK所在的文件路径,因为我们项目中还需要使用NDK。修改该文件,再文件后面加入如下内容,以便告诉IDE需要使用的NDK路径:ndk.dir=/YourPathToAndroidNDK/Android-NDK-r10d
- 修改
{project_home}/app/build.gradle
文件
在该文件中「适当的位置处」加入NDK配置内容如下:android { .... defaultConfig { ... ndk { moduleName "HelloJNI" stl "stlport_static" } } ... }
在文件中加入上述内容时,尤其需要注意
moduleName
属性设置的值,这里我将其命名为HelloJNI
,该名称可以随便写。在JNI代码中需要导入lib时将需要填写这个值(后续文章种将会提到)。 - 在
{project_home}/app/src/main
目录中新增jni
目录
在上述目录中新增一个jni
目录,并将编写好的MyClz.h
和MyClz.cpp
文件放入其中。 - 在
{project_home}/app/src/main/jni
目录中新增MyClz-JNI.cpp
文件
因为之前编写的MyClz.h
和MyClz.cpp
文件不符合JNI
的规范(客户提供的cpp文件很少会有符合JNI规范的),因此我们需要编写一个**符合JNI规范**的cpp文件。
到这一步时,{project_home}/app/src/main/jni
目录中应该包含了MyClz.h
、MyClz.cpp
及MyClz-JNI.cpp
三个文件。 - 编写
MyClz-JNI.cpp
文件的内容#include <jni.h> #include <stdio.h> #include <string.h> #include "MyClz.h" extern "C" { MyClz* clz; JNIEXPORT jdouble JNICALL Java_com_baratsemet_android_wrapper_HelloWrapper_getSumFromJNI(JNIEnv* env,jdouble num1, jdouble num2) { return clz->getSum(num1,num2); } };
请注意:
上述类中的方法名称,是**符合JNI规范**的方法名。该方法名必须是Java_包名_类名_方法名(参数名)
这种方式。若对此不明白的朋友,可以自行查阅JNI相关文档,这里就不在详述。
- 编写
HelloWrapper.java
文件
从上述MyClz-JNI.cpp
文件中的方法名中可以看出:我们将要编写的HelloWrapper.java
文件所在的包应该是:com.baratsemet.android.wrapper
,因此在{project_home}/app/main/java
中新建上述包,并在其中新建HelloWrapper.java
类文件,其内容如下:
public class HelloWrapper {
static {
//注意这里导入的类库名称与「步骤5」中的moduleName匹配
System.loadLibrary("HelloJNI");
}
public native double getSumFromJNI(double num1,double num2);
}
- 在菜单栏选择
Build
->Make Project
此时,选择Make Project
查看是不是有错误。若有错误,请查看日志以排除。 - 在需要的地方调用
HelloWrapper
类
疑问:
上述代码在「模拟器」和「真机」上调用时,结果可能不一致,大家想想,为什么呢?
在iOS设备调用
相对于Android而言,在iOS设备上调用C/C++代码就非常非常非常容易了。大致步骤如下:
- Xcode中新建一个iOS项目
新建一个名称HelloCPP
的iOS项目,先。选择开发语言时,可以选择Objective-C也可以选择Swift,反正都可以调用,反正都可以混合使用,随意啦! - 将
MyClz.h
和MyClz.cpp
文件拖入项目中
因为Xcode已经有一个main.m
文件作为项目的入口,因此请去掉MyClz.cpp
文件种的main函数
。 - 新建一个
Cocoa Class
并将其命名为MyClzWrapper
按下快捷键command + N
,再弹出的面板中选择新建「Cocoa Class」,并输入名称为:MyClzWrapper。
默认情况下,上述操作会生成一个MyClzWrapper.h
和MyClzWrapper.m
文件, 修改MyClzWrapper.m文件的后缀名为mm ,使该文件即能编写C/C++代码,也能编写Objective-C代码(也就是传说种的Objective-C++文件)MyClzWrapper.h
文件的内容:#import <Foundation/Foundation.h> @interface MyClzWrapper : NSObject +(double) getSumFromCPP:(double) number1 :(double) number2; @end
MyClzWrapper.mm
文件的内容:#import "MyClzWrapper.h" #import "MyClz.h" @implementation MyClzWrapper MyClz* clz = new MyClz(); +(double) getSumFromCPP:(double) number1 :(double) number2{ return clz->getSum(number1, number2); } @end
- 在需要的地方调用
MyClzWrapper.mm
文件
文章源码下载
请异步 git@oschina https://git.oschina.net/baratsemet/CPP_Andoird_iOS