Cocos2d-x 3.x Android Native Activity JNI 연동하기
Cocos2d-x 가 3.x가 되면서 기본적으로 아래 그림과 같이 안드로이드 자바 소스부분은 필요치 않게 변경이 되었습니다. 이는 안드로이드 Native Activity를 사용하게 되면서 이렇게 되었는데요, 이 부분에 있어서 요즘 이슈가 있는거 같아 R&D를 해봤습니다.
이렇게 새로운 프로젝트를 만들면 떡하니 nojava.txt가 있어서 당황하실 수 있습니다. 사실 java 파일을 넣어도 작업이 될 듯은 하네요. 해보진 않았지만요.
아래 정리할 내용은 Cocos2d-x 3.x에 간단하게 네이티브 액티비티와 간단한 JNI 처리하는 내용인데요, 사실 어떻게보면 기존에 정리했던 Cococs2d-x 1.x나 2.x 에서 처리했던 JNI 연동과 GLThread 이슈 처리와 비슷합니다. 왜냐면 Cocos2d-x에서 하듯이 정석?으로 안하고 간단히 샘플로 정리한 것이기 때문이죠. 정석은 밑에서 잠깐 소개하겠습니다. 또한 현재 3.0 alpha1까지 나왔는데 alpha0과는 폴더구조가 다릅니다. 아래 내용은 alpha1으로 진행한 내용입니다.
먼저 project-creator을 통해 아래와 같이 프로젝트명은 BasicNativeActivityJNI, 패키지명은 com.westwoodforever.sample.nativeactivityjni 로 만들었습니다.
C++에서 안드로이드 자바를 호출하기 위해 cocos2d-x-3.0alpha1\cocos\2d\platform\android\jni\Java_org_cocos2dx_lib_Cocos2dxHelper.cpp 를 수정해야 합니다.
void BasicJNICallTest(const char* pszMsg)
{
JniMethodInfo t;
//#define CLASS_NAME "org/cocos2dx/lib/Cocos2dxHelper"
if (JniHelper::getStaticMethodInfo(t, CLASS_NAME
, "AndroidJNIFuncCallTestFromCocos2dx3_0"
, "(Ljava/lang/String;)V")) {
jstring stringArg1;
stringArg1 = t.env->NewStringUTF(pszMsg);
t.env->CallStaticVoidMethod(t.classID, t.methodID, stringArg1);
t.env->DeleteLocalRef(stringArg1);
t.env->DeleteLocalRef(t.classID);
}
}
위와 같은 내용을 추가합니다. Cocos2dxHelper java파일의 AndroidJNIFuncCallTestFromCocos2dx3_0 이라는 함수를 호출해줍니다. 인자로 스트링 하나 넘기게 했습니다.
extern void BasicJNICallTest(const char* pszMsg);
Java_org_cocos2dx_lib_Cocos2dxHelper.h 파일에는 위 내용을 추가합니다.
이제 자바쪽에 JNI로 호출하는 함수를 정의합니다. 아래 그림과 같이 libcocos2dx/src/org.cocos2dx.lib에 있는 Cocos2dxHelper.java를 편집해야합니다.
public static void AndroidJNIFuncCallTestFromCocos2dx3_0(String strTest) {
nativeCocos2dx30CPPFunc(strTest + "->");
}
public static native void nativeCocos2dx30CPPFunc(String text);
위 내용을 추가합니다. 단순히 인자로 받은 스트링에 -> 를 추가해서 C++에 있는 네이티브 함수를 호출해줍니다.
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxHelper_nativeCocos2dx30CPPFunc(JNIEnv* env, jobject thiz, jstring strJNIMsg)
{
string strMsg = JniHelper::jstring2string(strJNIMsg);
string strFinal = strMsg + "Android JNI Call Test";
showDialogJNI(strFinal.c_str(), "WestwoodForever");
}
이제 다시 Java_org_cocos2dx_lib_Cocos2dxHelper.cpp 파일입니다. 위와 같은 내용을 extern "C" { } 안에 추가해줍니다. 안드로이드에서 받은 스트링에 내용을 추가해서 다시 안드로이드에서 메시지 박스 호출을 진행합니다.
// include 추가
#include "jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
void HelloWorld::menuCloseCallback(Object* pSender)
{
//Director::getInstance()->end();
BasicJNICallTest("Cocos2d-x3.0 JNI NativeC++");
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
마지막으로 HelloWorldScene.cpp에 종료 버튼 처리를 위와같이 수정해줍니다.
이제 아래와 같이 build_native.py를 한번 실행해서 C++을 빌드해주고 이클립스에서는 libcocos2dx와 샘플 프로젝트를 빌드 후 실행해줍니다.
그리고 실행하면 아래와 같은 결과를 볼 수 있습니다. 결과적으로 C++ 스트링 "Cocos2d-x3.0 JNI NativeC++" + 안드로이드 스트링 "->" + C++ 스트링 "Android JNI Call Test"를 가지고 안드로이드 메시지 박스를 출력했습니다.
지금까지 정리한 것을 가지고 프로젝트를 진행하는데 별 무리는 없을 듯 합니다. Cocos2d-x 3.x 버전 엔진딴에서 안드로이드 JNI 처리를 어떻게 했는지는 아래 그림과 같이 cocos\2d\platform\android 폴더에 있는 파일들을 분석해보시면 알 수 있습니다.
하지만 위에서도 말했듯이 이건 샘플수준이고 별로 건들기 싫은? Cococs2d-x 엔진 소스를 건들어줘야 하는 문제가 있습니다. Cocos2d-x 3.x 가 만들어놓은 틀안에서 이런 플러그인 작업을 하실려면 PluginManager 라는 것을 활용해서 해줘야 합니다. 이미 많은 플러그인들이 아래와 같이 통합되어 있습니다. 또한 plugin\samples\HelloPlugins 도 있으니 참고하시면 좋구요.
기본 구조는 플러그인 안드로이드 이클립스 프로젝트를 만들고 네이티브에서 사용하는 CPP 구조와 안드로이드 실제 플러그인 기능을 처리하는 java 파일들, JNI처리를 해줘야하는 protocol C++ 파일들을 처리해줘야 합니다. 기존에 그냥 안드로이드 Activity에서 처리하던 것을 의존성이 적게 하려고 이런식의 구조를 잡은 것 같습니다.
여담이지만 작년에 했던 Cocos2d-x 프레임워크가 위에서 설명한 PluginManager와 비슷한 구조로 작업을 했었습니다.
액티비티에는 PluginManager 역할을 하는 JniMapper라는 클래스를 한줄 초기화 해주고
JniMapper에서 C++과의 플러그인 메시지 처리를 해줬습니다.
저도 아래와 같이 플러그인들을 protocol로 만들어 처리했구요.
실제 JNI를 처리하는 파일도 따로 있었습니다. 이렇게해서 C++에서 사용하는 static lib인 framework.o 파일과 자바에서도 framework.jar로 따로 빼서 처리 했었죠.
PluginManager를 사용하는 방법은 아래 링크로 대체합니다. 저도 여유가 생기면 추가 정리를 해보겠습니다.
How to Integrate a 3rd party SDK into Plugin-X
Plugin-X Integration Guide for Android
How to do Java things with Android Native Activity
이렇게 새로운 프로젝트를 만들면 떡하니 nojava.txt가 있어서 당황하실 수 있습니다. 사실 java 파일을 넣어도 작업이 될 듯은 하네요. 해보진 않았지만요.
아래 정리할 내용은 Cocos2d-x 3.x에 간단하게 네이티브 액티비티와 간단한 JNI 처리하는 내용인데요, 사실 어떻게보면 기존에 정리했던 Cococs2d-x 1.x나 2.x 에서 처리했던 JNI 연동과 GLThread 이슈 처리와 비슷합니다. 왜냐면 Cocos2d-x에서 하듯이 정석?으로 안하고 간단히 샘플로 정리한 것이기 때문이죠. 정석은 밑에서 잠깐 소개하겠습니다. 또한 현재 3.0 alpha1까지 나왔는데 alpha0과는 폴더구조가 다릅니다. 아래 내용은 alpha1으로 진행한 내용입니다.
먼저 project-creator을 통해 아래와 같이 프로젝트명은 BasicNativeActivityJNI, 패키지명은 com.westwoodforever.sample.nativeactivityjni 로 만들었습니다.
C++에서 안드로이드 자바를 호출하기 위해 cocos2d-x-3.0alpha1\cocos\2d\platform\android\jni\Java_org_cocos2dx_lib_Cocos2dxHelper.cpp 를 수정해야 합니다.
void BasicJNICallTest(const char* pszMsg)
{
JniMethodInfo t;
//#define CLASS_NAME "org/cocos2dx/lib/Cocos2dxHelper"
if (JniHelper::getStaticMethodInfo(t, CLASS_NAME
, "AndroidJNIFuncCallTestFromCocos2dx3_0"
, "(Ljava/lang/String;)V")) {
jstring stringArg1;
stringArg1 = t.env->NewStringUTF(pszMsg);
t.env->CallStaticVoidMethod(t.classID, t.methodID, stringArg1);
t.env->DeleteLocalRef(stringArg1);
t.env->DeleteLocalRef(t.classID);
}
}
위와 같은 내용을 추가합니다. Cocos2dxHelper java파일의 AndroidJNIFuncCallTestFromCocos2dx3_0 이라는 함수를 호출해줍니다. 인자로 스트링 하나 넘기게 했습니다.
extern void BasicJNICallTest(const char* pszMsg);
Java_org_cocos2dx_lib_Cocos2dxHelper.h 파일에는 위 내용을 추가합니다.
이제 자바쪽에 JNI로 호출하는 함수를 정의합니다. 아래 그림과 같이 libcocos2dx/src/org.cocos2dx.lib에 있는 Cocos2dxHelper.java를 편집해야합니다.
public static void AndroidJNIFuncCallTestFromCocos2dx3_0(String strTest) {
nativeCocos2dx30CPPFunc(strTest + "->");
}
public static native void nativeCocos2dx30CPPFunc(String text);
위 내용을 추가합니다. 단순히 인자로 받은 스트링에 -> 를 추가해서 C++에 있는 네이티브 함수를 호출해줍니다.
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxHelper_nativeCocos2dx30CPPFunc(JNIEnv* env, jobject thiz, jstring strJNIMsg)
{
string strMsg = JniHelper::jstring2string(strJNIMsg);
string strFinal = strMsg + "Android JNI Call Test";
showDialogJNI(strFinal.c_str(), "WestwoodForever");
}
이제 다시 Java_org_cocos2dx_lib_Cocos2dxHelper.cpp 파일입니다. 위와 같은 내용을 extern "C" { } 안에 추가해줍니다. 안드로이드에서 받은 스트링에 내용을 추가해서 다시 안드로이드에서 메시지 박스 호출을 진행합니다.
// include 추가
#include "jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
void HelloWorld::menuCloseCallback(Object* pSender)
{
//Director::getInstance()->end();
BasicJNICallTest("Cocos2d-x3.0 JNI NativeC++");
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
마지막으로 HelloWorldScene.cpp에 종료 버튼 처리를 위와같이 수정해줍니다.
이제 아래와 같이 build_native.py를 한번 실행해서 C++을 빌드해주고 이클립스에서는 libcocos2dx와 샘플 프로젝트를 빌드 후 실행해줍니다.
그리고 실행하면 아래와 같은 결과를 볼 수 있습니다. 결과적으로 C++ 스트링 "Cocos2d-x3.0 JNI NativeC++" + 안드로이드 스트링 "->" + C++ 스트링 "Android JNI Call Test"를 가지고 안드로이드 메시지 박스를 출력했습니다.
지금까지 정리한 것을 가지고 프로젝트를 진행하는데 별 무리는 없을 듯 합니다. Cocos2d-x 3.x 버전 엔진딴에서 안드로이드 JNI 처리를 어떻게 했는지는 아래 그림과 같이 cocos\2d\platform\android 폴더에 있는 파일들을 분석해보시면 알 수 있습니다.
하지만 위에서도 말했듯이 이건 샘플수준이고 별로 건들기 싫은? Cococs2d-x 엔진 소스를 건들어줘야 하는 문제가 있습니다. Cocos2d-x 3.x 가 만들어놓은 틀안에서 이런 플러그인 작업을 하실려면 PluginManager 라는 것을 활용해서 해줘야 합니다. 이미 많은 플러그인들이 아래와 같이 통합되어 있습니다. 또한 plugin\samples\HelloPlugins 도 있으니 참고하시면 좋구요.
기본 구조는 플러그인 안드로이드 이클립스 프로젝트를 만들고 네이티브에서 사용하는 CPP 구조와 안드로이드 실제 플러그인 기능을 처리하는 java 파일들, JNI처리를 해줘야하는 protocol C++ 파일들을 처리해줘야 합니다. 기존에 그냥 안드로이드 Activity에서 처리하던 것을 의존성이 적게 하려고 이런식의 구조를 잡은 것 같습니다.
여담이지만 작년에 했던 Cocos2d-x 프레임워크가 위에서 설명한 PluginManager와 비슷한 구조로 작업을 했었습니다.
액티비티에는 PluginManager 역할을 하는 JniMapper라는 클래스를 한줄 초기화 해주고
JniMapper에서 C++과의 플러그인 메시지 처리를 해줬습니다.
저도 아래와 같이 플러그인들을 protocol로 만들어 처리했구요.
실제 JNI를 처리하는 파일도 따로 있었습니다. 이렇게해서 C++에서 사용하는 static lib인 framework.o 파일과 자바에서도 framework.jar로 따로 빼서 처리 했었죠.
PluginManager를 사용하는 방법은 아래 링크로 대체합니다. 저도 여유가 생기면 추가 정리를 해보겠습니다.
How to Integrate a 3rd party SDK into Plugin-X
Plugin-X Integration Guide for Android
How to do Java things with Android Native Activity
댓글
댓글 쓰기