Android와 iOS에서 동일한 C++ 코드를 사용하는 방법은 무엇입니까?
NDK를 탑재한 Android는 C/C++ 코드를 지원하며, Objective-C++를 탑재한 iOS도 지원하는데, Android와 iOS 간에 공유되는 네이티브 C/C++ 코드를 사용하여 애플리케이션을 작성하려면 어떻게 해야 합니까?
갱신하다.
이 답변은 제가 쓴 지 4년이 지나도 꽤 인기가 있는데, 이 4년 동안 많은 것이 바뀌었기 때문에 저는 현재의 현실에 맞게 답변을 업데이트하기로 했습니다.답변 아이디어는 변경되지 않습니다. 구현이 약간 변경되었습니다.제 영어도 많이 바뀌었고, 많이 늘어서 이제는 누구나 답을 이해할 수 있습니다.
아래의 코드를 다운로드하여 실행할 수 있도록 리포(repo)를 봐주세요.
정답
코드를 보여주기 전에 아래 그림을 많이 봐주세요.
각 OS에는 UI와 특성이 있기 때문에 각 플랫폼에 특정 코드를 작성할 예정입니다.한편, 모든 로직 코드, 비즈니스 규칙, 공유할 수 있는 것은 C++를 사용하여 작성할 예정이므로 각 플랫폼에 동일한 코드를 컴파일할 수 있습니다.
그림에서 가장 낮은 레벨에 C++ 레이어가 표시됩니다.모든 공유 코드는 이 세그먼트에 있습니다.가장 높은 레벨은 일반 Obj-C/Java/Kotlin 코드입니다.여기서는 뉴스가 없습니다.하드한 부분은 중간 레이어입니다.
중간 레이어에서 iOS 측으로 가는 방법은 간단합니다.Obj-c를 사용하여 빌드하도록 프로젝트를 설정하면 됩니다.Obj-c는 Objective-C++로 알려져 있습니다.다만, C++ 코드에 액세스 할 수 있습니다.
안드로이드 측에서는 Java와 Kotlin 두 언어 모두 Java Virtual Machine에서 실행되므로 더욱 어려워졌습니다.따라서 C++코드에 접속하는 유일한 방법은 JNI를 사용하는 것입니다.JNI의 기본을 읽어보시기 바랍니다.다행히 오늘날의 Android Studio IDE는 JNI 측면에서 크게 개선되었으며 코드를 편집하는 동안 많은 문제가 나타납니다.
단계별 코드
샘플은 CPP에 텍스트를 보내는 간단한 앱으로, 그 텍스트를 다른 것으로 변환하여 반송합니다.iOS는 "Obj-C"를 전송하고 Android는 "Java"를 각 언어에서 전송하며 CPP 코드는 "cpp says hello to < text received >"라는 후속으로 텍스트를 생성합니다.
공유 CPP 코드
우선 공유 CPP 코드를 만듭니다.그렇게 하면 원하는 텍스트를 수신하는 메서드 선언을 가진 단순한 헤더파일이 생성됩니다.
#include <iostream>
const char *concatenateMyStringWithCppString(const char *myString);
CPP의 실장은 다음과 같습니다.
#include <string.h>
#include "Core.h"
const char *CPP_BASE_STRING = "cpp says hello to %s";
const char *concatenateMyStringWithCppString(const char *myString) {
char *concatenatedString = new char[strlen(CPP_BASE_STRING) + strlen(myString)];
sprintf(concatenatedString, CPP_BASE_STRING, myString);
return concatenatedString;
}
유닉스
흥미로운 점은 Linux와 Mac뿐만 아니라 다른 Unix 시스템에도 동일한 코드를 사용할 수 있다는 것입니다.공유 코드를 보다 빠르게 테스트할 수 있기 때문에 이 가능성은 특히 유용합니다.따라서 다음과 같이 Main.cpp를 작성하여 머신에서 실행하여 공유 코드가 동작하고 있는지 확인합니다.
#include <iostream>
#include <string>
#include "../CPP/Core.h"
int main() {
std::string textFromCppCore = concatenateMyStringWithCppString("Unix");
std::cout << textFromCppCore << '\n';
return 0;
}
코드를 작성하려면 다음을 실행해야 합니다.
$ g++ Main.cpp Core.cpp -o main
$ ./main
cpp says hello to Unix
iOS
이제는 모바일 측면에서 구현할 때입니다.은 이것부터 합니다.OS에 심플한 통합이 있는 한, 우선은 이것부터 시작합니다.은 하나의은 iOS Obj-c입니다. 을 사용하다.mm
and.m
앱이
보다 나은 조직을 위해 다음과 같이 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)로 사용할 수 있습니다.
#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는 빌드 시스템으로서 Gradle을 사용하고, C/C++ 코드는 CMake를 사용하고 있습니다.따라서 첫 번째로 그래들파일에 CMake를 설정할 필요가 있습니다.
android {
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
...
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++14"
}
}
...
}
두 번째 단계는 CMake Lists를 추가하는 것입니다.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++ 설정에 대한 자세한 내용은 여기를 참조하십시오.
이제 핵심 코드가 앱의 일부가 되었습니다.브릿지를 만들고, 작업을 보다 심플하고 정리하기 위해서, JVM과 CPP 사이에 래퍼로서 CoreWrapper라는 이름의 특정 클래스를 만듭니다.
public class CoreWrapper {
public native String concatenateMyStringWithCppString(String myString);
static {
System.loadLibrary("native-lib");
}
}
에는 「」가 .native
합니다.native-lib
델이 는 공유 오브젝트 「」가 됩니다.「」 「CPP」 「CPP」 「CPP」.so
삽입 및 APK에 파일 loadLibrary
로드된 됩니다.마지막으로 네이티브 메서드를 호출하면 JVM은 로드된 라이브러리에 콜을 위임합니다.
Android 통합의 가장 이상한 부분은 JNI입니다.여기서 native-lib.cpp 라고 하는 cpp 파일이 필요합니다.
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;
}
}
에 띄는 은 '아예'입니다.extern "C"
이 파트는 JNI가 CPP 코드와 메서드 링크에서 올바르게 동작하기 위해 필요합니다.을 JNI JVM으로 몇 가지 됩니다.JNIEXPORT
★★★★★★★★★★★★★★★★★」JNICALL
이 튜토리얼에서는 이것들을 보일러 플레이트로 간주하고 있기 때문에, 그 의미를 이해하기 위해서는, 시간을 들여 읽어 볼 필요가 있습니다.
중요한 것 중 하나이며 보통 많은 문제의 근원은 메서드의 이름입니다.메서드는 "Java_package_class_method" 패턴을 따라야 합니다.현재 Android Studio는 이를 매우 잘 지원하고 있기 때문에 이 보일러 플레이트를 자동으로 생성하여 정확한지 아닌지를 보여줄 수 있습니다.이 예에서는 "Java_ademar_androidioscppample_CoreWrapper_connectateMyStringWithCppString"이라는 이름의 메서드가 있습니다.이는 "ademar.androidioscppexample"이 패키지이기 때문에 "_"로 대체됩니다.CoreWrapper는 네이티브 메서드와 링크된 클래스입니다.
메서드는 인수를 되어 있기 첫는 '인수 분석'의 입니다.JNIEnv
JNI에 접속할 수 있는 방법이기 때문에 곧 알게 될 것처럼 전환에 매우 중요합니다. the the입니다.jobject
이 메서드를 호출하기 위해 사용한 객체의 인스턴스입니다.Java "this"라고 생각하시면 됩니다.이 예에서는 사용할 필요가 없지만 선언할 필요가 있습니다.이 jobject 후에 메서드의 인수를 받습니다.이 메서드에는 "myString"이라는 인수가1개밖에 없기 때문에 같은 이름의 "jstring"만 있습니다.또한 우리의 리턴 타입도 jstring이라는 점에 주의해 주십시오.Java/J에 대한 자세한 내용은 Java 메서드가 문자열을 반환하기 때문입니다.NI 타입은 읽어주세요.
마지막 단계는 JNI 유형을 CPP 측에서 사용하는 유형으로 변환하는 것입니다.이 예에서는, 델에서는,jstring
에 대해서const char *
CPP로 변환하여 결과를 가져오고 다시 변환합니다.jstring
다른 로 어려운되어 은 JNI에 .보일러플레이트만 되어 있고 모든 작업은JNIEnv*
를 할 때 GetStringUTFChars
★★★★★★★★★★★★★★★★★」NewStringUTF
그 후, Android 디바이스에서 실행할 수 있는 코드가 준비되었습니다.자, 봅시다.
위의 우수한 답변에 설명된 접근법은 C++ 헤더에서 래퍼 코드를 바로 생성하는 Scapix Language Bridge에 의해 완전히 자동화될 수 있습니다.다음은 예를 제시하겠습니다.
C++에서 클래스를 정의합니다.
#include <scapix/bridge/object.h>
class contact : public scapix::bridge::object<contact>
{
public:
std::string name();
void send_message(const std::string& msg, std::shared_ptr<contact> from);
void add_tags(const std::vector<std::string>& tags);
void add_friends(std::vector<std::shared_ptr<contact>> friends);
};
Swift에서 불러주세요.
class ViewController: UIViewController {
func send(friend: Contact) {
let c = Contact()
contact.sendMessage("Hello", friend)
contact.addTags(["a","b","c"])
contact.addFriends([friend])
}
}
Java에서:
class View {
private contact = new Contact;
public void send(Contact friend) {
contact.sendMessage("Hello", friend);
contact.addTags({"a","b","c"});
contact.addFriends({friend});
}
}
언급URL : https://stackoverflow.com/questions/18334547/how-to-use-the-same-c-code-for-android-and-ios
'itsource' 카테고리의 다른 글
auto_increment id가 1개씩 증가하지 않는 이유는 무엇입니까?설정 방법은? (0) | 2022.11.24 |
---|---|
부호 있는 정수에 대한 비트 연산 결과가 정의되어 있습니까? (0) | 2022.11.24 |
MySQL 여러 테이블에 삽입? (데이터베이스 정규화?) (0) | 2022.11.24 |
MariaDB - 두 개의 외부 키로 테이블을 만들 수 없습니다. (0) | 2022.11.24 |
TokuDB 쿼리에서 임시 테이블을 만드는 속도가 너무 느립니다. (0) | 2022.11.24 |