不少Android和iOS项目中,因为种种原因不得不调用C/C++代码。这篇文章主要讲述如何通过Objective-C++、NDK技术在iOS及Android设备上调用C/C++代码。
通过这篇文章对我的启发是:
可以通过C++写一个socket长连接的功能模块。可以通过这种方法来实现安卓和苹果都能共用这个模块,不用安卓和苹果各自开发长连接模块了。
socket长连接对一部分人本来就陌生。并且维护一份代码比维护两份代码要容易的多。
以后我要把我的Object C写的socket长连接转换成C++的长连接。哥本来就是C++出身转Objec C的。
转载文章地址:https://my.oschina.net/baratsemet/blog/419692
主要工作原理
主要工作原理,如上图所示。Google Android提供NDK以便支持C/C++代码,而iOS可以通过Objective-C++(*.mm)来支持C/C++代码的编译运行。如果有朋友钻研过Cocos2d-x的话,应该对上述这些内容非常熟悉。好了,废话不多说,开始提供干货。
需要调用的C++代码
我们先编写一个简单的C++代码,以便能够在Android和iOS设备上调用。为了简单起见,也为了能够充分展示操作过程,我们设计如下的C++类:
MyClz.h文件
include
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设备调用
1.下载并配置Android Studio
本例中,采用了最新版的Android Studio(v1.2.1.1)作为开发环境。下载并安装,同时根据自己的需要下载相应的Android SDK并配置好各项开发环境。
请注意:如下网页需要「科学上网」才能正常访问
Android Studio:http://developer.android.com/sdk/index.html
2.下载并配置NDK
NDK是Google提供的一套用以在Android设备上调用C/C++代码的工具集。某些情况下,可能没有办法使用Java语言完成的任务,只能借助底层的本地代码(native code)实现。这时,NDK就会派上用场。到如下地址下载NDK并将其解压到本地磁盘的某个路径即可。
请注意:如下页面需要「科学上网」才能正常访问
Android NDK:http://developer.android.com/tools/sdk/ndk/index.html
3.新建一个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路径配置文件
4.修改{project_home}/local.properties文件
该文件种默认有一行,是指定Android SDK所在的文件路径,因为我们项目中还需要使用NDK。修改该文件,再文件后面加入如下内容,以便告诉IDE需要使用的NDK路径:
ndk.dir=/YourPathToAndroidNDK/Android-NDK-r10d
5.修改{project_home}/app/build.gradle文件
在该文件中「适当的位置处」加入NDK配置内容如下:
android {
….
defaultConfig {
…
ndk {
moduleName “HelloJNI”
stl “stlport_static”
}
}
…
}
在文件中加入上述内容时,尤其需要注意moduleName属性设置的值,这里我将其命名为HelloJNI,该名称可以随便写。在JNI代码中需要导入lib时将需要填写这个值(后续文章种将会提到)。
6.在{project_home}/app/src/main目录中新增jni目录
在上述目录中新增一个jni目录,并将编写好的MyClz.h和MyClz.cpp文件放入其中。
7.在{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三个文件。
8.编写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);
}
1.在菜单栏选择Build->Make Project
此时,选择Make Project查看是不是有错误。若有错误,请查看日志以排除。
2.在需要的地方调用HelloWrapper类
疑问:
上述代码在「模拟器」和「真机」上调用时,结果可能不一致,大家想想,为什么呢?
在iOS设备调用
相对于Android而言,在iOS设备上调用C/C++代码就非常非常非常容易了。大致步骤如下:
1.Xcode中新建一个iOS项目
新建一个名称HelloCPP的iOS项目,先。选择开发语言时,可以选择Objective-C也可以选择Swift,反正都可以调用,反正都可以混合使用,随意啦!
2.将MyClz.h和MyClz.cpp文件拖入项目中
因为Xcode已经有一个main.m文件作为项目的入口,因此请去掉MyClz.cpp文件种的main函数。
3.新建一个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
4.在需要的地方调用MyClzWrapper.mm文件