Android 및 iOS에 동일한 C ++ 코드를 사용하는 방법은 무엇입니까?
NDK 를 사용하는 Android 는 C / C ++ 코드 를 지원 하고 Objective-C ++ 를 사용하는 iOS 도 지원하므로 Android와 iOS간에 공유되는 기본 C / C ++ 코드로 애플리케이션을 작성하려면 어떻게해야합니까?
최신 정보.
이 답변은 내가 작성한 후 4 년이 지나도 꽤 인기가 있습니다.이 4 년 동안 많은 것들이 바뀌어 현재의 현실에 더 잘 맞도록 답변을 업데이트하기로 결정했습니다. 답은 바뀌지 않습니다. 구현이 약간 변경되었습니다. 내 영어도 바뀌었고 많이 향상되었으므로 이제 모든 사람이 대답을 더 잘 이해할 수 있습니다.
아래에 표시된 코드를 다운로드하여 실행할 수 있도록 리포지토리를 살펴보십시오 .
대답
코드를 보여주기 전에 다음 다이어그램을 많이 살펴보십시오.
각 OS에는 UI와 특성이 있으므로 이와 관련하여 각 플랫폼에 특정 코드를 작성하려고합니다. 반면에 모든 논리 코드, 비즈니스 규칙 및 공유 할 수있는 것은 C ++을 사용하여 작성하려고하므로 각 플랫폼에 동일한 코드를 컴파일 할 수 있습니다.
다이어그램에서 가장 낮은 수준에서 C ++ 계층을 볼 수 있습니다. 모든 공유 코드는이 세그먼트에 있습니다. 최상위 수준은 일반적인 Obj-C / Java / Kotlin 코드이며, 뉴스는 없습니다. 어려운 부분은 중간 계층입니다.
iOS 측의 중간 계층은 간단합니다. Objective-C ++ 로 알려진 Obj-c의 변형을 사용하여 빌드하도록 프로젝트를 구성하기 만하면 되며 C ++ 코드에 액세스 할 수 있습니다.
안드로이드에서 Java와 Kotlin은 Java Virtual Machine에서 실행되는 언어가 더 어려워졌습니다. 액세스 C ++ 코드 유일한 방법은 사용하고 그래서 JNI를 , JNI의 기초를 읽을 시간이 걸릴하시기 바랍니다. 다행히도 오늘날의 Android Studio IDE는 JNI 측면에서 크게 개선되었으며 코드를 편집하는 동안 많은 문제가 표시됩니다.
단계별 코드
이 샘플은 텍스트를 CPP로 보내는 간단한 앱으로 해당 텍스트를 다른 것으로 변환하여 반환합니다. 아이디어는 아이폰 OS가 "의 Obj-C"를 보내드립니다 안드로이드는 각각의 언어에서 "자바"를 보낼 것이며, CPP 코드는 다음과 같은 텍스트를 만들 것입니다 "CPP는에 안부 << 텍스트 >> 수신 된 ".
공유 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에 동일한 코드를 사용할 수도 있다는 것입니다. 이 가능성은 공유 코드를 더 빠르게 테스트 할 수 있기 때문에 특히 유용합니다. 따라서 머신에서 실행하고 공유 코드가 작동하는지 확인하기 위해 다음과 같이 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
모바일 측면에서 구현할 때입니다. iOS가 간단한 통합을하는 한 우리는 그것부터 시작합니다. iOS 앱은 한 가지 차이점 만있는 일반적인 Obj-c 앱입니다. 파일은 .mm
그렇지 않습니다 .m
. 즉, 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 ").
#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 on gradle 파일을 구성하는 것입니다.
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 코드는 .so
APK에 공유 객체 File embed가되어 loadLibrary
로드합니다. 마지막으로 기본 메소드를 호출하면 JVM이로드 된 라이브러리에 호출을 위임합니다.
이제 안드로이드 통합의 가장 이상한 부분은 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 as JNIEXPORT
및 로 작업하는 데 사용하는 일부 기호가 표시됩니다 JNICALL
. 이러한 것들의 의미를 이해하려면 시간이 걸리고 그것을 읽어야합니다 .이 튜토리얼에서는 이러한 것들을 상용구로 간주하면됩니다.
중요한 문제 중 하나는 대개 방법의 이름입니다. "Java_package_class_method"패턴을 따라야합니다. 현재 Android Studio는이를 완벽하게 지원 하므로이 상용구를 자동으로 생성하고 이름이 정확하지 않은 경우 표시 할 수 있습니다. 이 예제에서 메소드 이름은 "Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString"입니다. 이는 "ademar.androidioscppexample"이 우리의 패키지이기 때문에 "."를 대체합니다. "_"에 의해 CoreWrapper는 기본 메소드를 링크하는 클래스이고 "concatenateMyStringWithCppString"은 메소드 이름 자체입니다.
메소드가 올바로 선언되었다는 것을 선언 했으므로 첫 번째 매개 변수는 JNIEnv
JNI에 액세스 할 수있는 방법을 가리키는 포인터입니다 . 조만간 변환하는 것이 중요합니다. 두 번째는 jobject
이 메소드를 호출하는 데 사용한 객체의 인스턴스입니다. 당신은 그것을 자바 " this " 라고 생각할 수있다. 예제에서 우리는 그것을 사용할 필요는 없지만 여전히 선언 할 필요가있다. 이 jobject 이후에, 우리는 메소드의 인수를받을 것입니다. 우리의 메소드는 하나의 인수, 즉 문자열 "myString"을 가지기 때문에 같은 이름을 가진 "jstring"만 있습니다. 또한 반환 유형도 jstring입니다. Java 메소드가 문자열을 리턴하기 때문에 Java / JNI 유형에 대한 자세한 정보는 문자열을 읽으십시오.
마지막 단계는 JNI 유형을 CPP 측에서 사용하는 유형으로 변환하는 것입니다. 이 예에서는 CPP로 변환 jstring
된 const char *
전송으로 변환하여 결과를 가져오고 다시 변환합니다 jstring
. JNI의 다른 모든 단계와 마찬가지로 어렵지 않습니다. 그것은 보일러 플레이트 일 뿐이며, 모든 작업은 and를 JNIEnv*
호출 할 때받는 인수에 의해 수행됩니다 . 코드가 Android 기기에서 실행될 준비가되면 살펴 보겠습니다.GetStringUTFChars
NewStringUTF
위의 훌륭한 답변에 설명 된 접근법 은 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])
}
}
그리고 자바에서 :
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
'Programing' 카테고리의 다른 글
Python 요청 라이브러리의 get 메소드와 함께 헤더 사용 (0) | 2020.07.21 |
---|---|
현재 노드 버전 확인 (0) | 2020.07.21 |
“pip”로 패키지를 제거하면 종속 패키지도 제거됩니까? (0) | 2020.07.21 |
팬더 데이터 프레임 인덱스에 값이 있는지 확인하십시오. (0) | 2020.07.21 |
Java 메소드에서 쓸모없는 리턴을 피하려면 어떻게합니까? (0) | 2020.07.20 |