在Android和iOS设备上调用C++代码

不少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设备调用

  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.hMyClz.cpp文件放入其中。

  7. {project_home}/app/src/main/jni目录中新增MyClz-JNI.cpp文件
    因为之前编写的MyClz.hMyClz.cpp文件不符合JNI的规范(客户提供的cpp文件很少会有符合JNI规范的),因此我们需要编写一个**符合JNI规范**的cpp文件。
    到这一步时,{project_home}/app/src/main/jni目录中应该包含了MyClz.hMyClz.cppMyClz-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相关文档,这里就不在详述。

  1. 编写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.hMyClz.cpp文件拖入项目中
    因为Xcode已经有一个main.m文件作为项目的入口,因此请去掉MyClz.cpp文件种的main函数

  3. 新建一个Cocoa Class并将其命名为MyClzWrapper
    按下快捷键command + N,再弹出的面板中选择新建「Cocoa Class」,并输入名称为:MyClzWrapper。
    默认情况下,上述操作会生成一个MyClzWrapper.hMyClzWrapper.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文件

文章源码下载

请异步 git@oschina https://git.oschina.net/baratsemet/CPP_Andoird_iOS

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。