Java 如何使用相同的C++;Android和iOS的代码?

Java 如何使用相同的C++;Android和iOS的代码?,java,c++,java-native-interface,cross-platform,objective-c++,Java,C++,Java Native Interface,Cross Platform,Objective C++,Android with支持C/C++代码,iOS with也支持,那么我如何使用Android和iOS之间共享的本机C/C++代码编写应用程序?Update。 这个答案在我写了四年后仍然很流行,在这四年里,很多事情都发生了变化,所以我决定更新我的答案,以更好地适应我们当前的现实。答案观念没有改变;实现有了一些变化。我的英语也变了,进步了很多,所以现在每个人都能理解答案了 请看一下,这样您就可以下载并运行我将在下面展示的代码 答案 在我展示代码之前,请仔细阅读下图 每个操作系统都有自己的UI和

Android with支持C/C++代码,iOS with也支持,那么我如何使用Android和iOS之间共享的本机C/C++代码编写应用程序?

Update。 这个答案在我写了四年后仍然很流行,在这四年里,很多事情都发生了变化,所以我决定更新我的答案,以更好地适应我们当前的现实。答案观念没有改变;实现有了一些变化。我的英语也变了,进步了很多,所以现在每个人都能理解答案了

请看一下,这样您就可以下载并运行我将在下面展示的代码

答案 在我展示代码之前,请仔细阅读下图

每个操作系统都有自己的UI和特性,因此我们打算在这方面为每个平台编写特定的代码。在其他方面,所有逻辑代码、业务规则和可以共享的东西,我们打算用C++编写,这样我们就可以编译到每个平台上相同的代码。

在图表中,你可以看到C++层在最低级。所有共享代码都在此段中。最高级别是常规的Obj-C/Java/Kotlin代码,这里没有新闻,最难的部分是中间层

iOS端的中间层很简单;您只需要使用Obj-C的变体来配置项目,就可以访问C++代码。 在Android方面,事情变得更难了,Android上的Java和Kotlin两种语言都在Java虚拟机下运行。所以访问C++代码的唯一方法是使用,请花时间阅读JNI的基础知识。幸运的是,今天的androidstudioide在JNI方面有了巨大的改进,在编辑代码时会向您显示许多问题

代码是按步骤编写的 我们的示例是一个简单的应用程序,您可以将文本发送到CPP,然后它将该文本转换为其他文本并返回。这个想法是,iOS将发送“Obj-C”,Android将从各自的语言发送“Java”,CPP代码将创建一个文本,如下所示:“CPP向>”打招呼

共享CPP代码 首先,我们将创建共享CPP代码,这样我们就有了一个简单的头文件,其中包含接收所需文本的方法声明:

#include <iostream>

const char *concatenateMyStringWithCppString(const char *myString);
网间网操作系统 现在是在移动端实施的时候了。只要iOS有一个简单的集成,我们就从它开始。我们的iOS应用程序是典型的Obj-c应用程序,只有一个区别;这些文件是
.mm
而不是
.m
。i、 e.它是一个Obj-C++应用程序,而不是Obj-C应用程序

为了更好地组织,我们创建CoreWrapper.mm,如下所示:

#import "CoreWrapper.h"

@implementation CoreWrapper

+ (NSString*) concatenateMyStringWithCppString:(NSString*)myString {
    const char *utfString = [myString UTF8String];
    const char *textFromCppCore = concatenateMyStringWithCppString(utfString);
    NSString *objcString = [NSString stringWithUTF8String:textFromCppCore];
    return objcString;
}

@end
此类负责将CPP类型和调用转换为Obj-C类型和调用。一旦您可以在Obj-C上的任何文件上调用CPP代码,它就不是强制性的,但它有助于保持组织,并且在包装文件之外,您维护一个完整的Obj-C样式代码,只有包装文件成为CPP样式

包装器连接到CPP代码后,可以将其用作标准Obj-C代码,例如ViewController“

看看应用程序的外观:

安卓 现在是Android集成的时候了。Android使用Gradle作为构建系统,在C/C++代码中使用CMake。因此,我们需要做的第一件事是在Gradle文件上配置CMake:

android {
...
externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}
...
defaultConfig {
    externalNativeBuild {
        cmake {
            cppFlags "-std=c++14"
        }
    }
...
}
第二步是添加CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.4.1)

include_directories (
    ../../CPP/
)

add_library(
    native-lib
    SHARED
    src/main/cpp/native-lib.cpp
    ../../CPP/Core.h
    ../../CPP/Core.cpp
)

find_library(
    log-lib
    log
)

target_link_libraries(
    native-lib
    ${log-lib}
)
在CMake文件中,您需要添加将在项目中使用的CPP文件和头文件夹。在我们的示例中,我们添加了
CPP
文件夹和Core.h/.CPP文件。要了解有关C/C++配置的更多信息,请

现在核心代码是我们应用程序的一部分,是时候创建桥梁了,为了使事情更加简单和有组织,我们创建了一个名为CoreWrapper的特定类作为JVM和CPP之间的包装器:

public class CoreWrapper {

    public native String concatenateMyStringWithCppString(String myString);

    static {
        System.loadLibrary("native-lib");
    }

}
注意:这个类有一个
native
方法,并加载一个名为
native lib
的本机库。这个库是我们创建的,最后,CPP代码将成为一个共享对象
。因此
文件嵌入到我们的APK中,
loadLibrary
将加载它。最后,当您调用本机方法时,JVM将委托调用已加载的库

现在Android集成最奇怪的部分是JNI;我们需要一个cpp文件,在我们的例子“native lib.cpp”中如下所示:

您首先会注意到的是
extern”C“
这一部分是JNI正确使用我们的CPP代码和方法链接所必需的。您还将看到JNI用于JVM的一些符号,如
JNIEXPORT
JNICALL
。为了了解这些东西的含义,有必要花些时间,而对于本教程的目的,只是考虑这些事情作为样板。

一件重要的事情,通常是许多问题的根源是方法的名称;它需要遵循“Java\u包\u类\u方法”模式。目前,androidstudio对它有很好的支持,因此它可以自动生成这个样板文件,并在正确或未命名时向您显示。在我们的示例中,我们的方法被命名为“Java_ademar_androidioscppexample_CoreWrapper_concatenemystringwithcppstring”,这是因为“ademar.androidioscppexample”是我们的包,所以我们用“_”替换“.”,CoreWrapper是我们链接本机方法的类,“concatenemystringwithcppstring”是方法名称本身

由于我们已经正确地声明了方法,现在是分析参数的时候了,第一个参数是
JNIEnv
的指针。这是我们访问JNI内容的方式,我们进行转换至关重要,您很快就会看到。第二个是
jobject
,它是用于调用此方法的对象的实例。您可以将其视为java“this”,在我们的示例中,我们不需要使用它,但仍然需要声明它。在这个jobject之后,我们将接收该方法的参数。因为我们的方法只有一个参数
#import "ViewController.h"
#import "CoreWrapper.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UILabel *label;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString* textFromCppCore = [CoreWrapper concatenateMyStringWithCppString:@"Obj-C++"];
    [_label setText:textFromCppCore];
}

@end
android {
...
externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}
...
defaultConfig {
    externalNativeBuild {
        cmake {
            cppFlags "-std=c++14"
        }
    }
...
}
cmake_minimum_required(VERSION 3.4.1)

include_directories (
    ../../CPP/
)

add_library(
    native-lib
    SHARED
    src/main/cpp/native-lib.cpp
    ../../CPP/Core.h
    ../../CPP/Core.cpp
)

find_library(
    log-lib
    log
)

target_link_libraries(
    native-lib
    ${log-lib}
)
public class CoreWrapper {

    public native String concatenateMyStringWithCppString(String myString);

    static {
        System.loadLibrary("native-lib");
    }

}
extern "C" {

JNIEXPORT jstring JNICALL Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString(JNIEnv *env, jobject /* this */, jstring myString) {
    const char *utfString = env->GetStringUTFChars(myString, 0);
    const char *textFromCppCore = concatenateMyStringWithCppString(utfString);
    jstring javaString = env->NewStringUTF(textFromCppCore);
    return javaString;
}

}
class ViewController: UIViewController {
    func send(friend: Contact) {
        let c = Contact()

        contact.sendMessage("Hello", friend)
        contact.addTags(["a","b","c"])
        contact.addFriends([friend])
    }
}
class View {
    private contact = new Contact;

    public void send(Contact friend) {
        contact.sendMessage("Hello", friend);
        contact.addTags({"a","b","c"});
        contact.addFriends({friend});
    }
}