IT_Games/Cocos2D

[펌] cocos2d-x JNI를 가지고 android에서 Native Code(C/C++) ↔ Java 양방향 호출해보자

JJun ™ 2012. 9. 28. 03:36



 출처

 : https://westwoodforever.blogspot.kr/2012/09/cocos2d-x-jni-android-native-codecc-java.html





cocos2d-x에 JIN를 사용해서 C/C++ <-> Java 양방향 호출을 알아보기 전에 간단하게 JNI가 무엇인지 리서치 해본 것을 먼저 정리해보겠습니다.

Java에서 특정 플랫폼에서 실행 가능한 네이티브 코드에 접근하기 위해 만들어진 API를 JNI(Java Native Interface)라고 합니다. 

Java에는 이미 많은 모듈들이 있지만 이것으로는 구현할 수 없거나, 조금더 Low Level단 Hardware접근이나 Performance를 요구할 때 

해당 플랫폼에 종속된 언어, 예를 들자면  C/C++ 로 구현 된 모듈에 접근할 때 사용한다고 하네요.

cocos2d-x에서 JNI를 사용해야 할 때는 언제일까요? 먼저 Java에서 C/C++ 로는 뭐가 있을까요? 

In App Purphase에 대한 정보를 Java에서 게임에 반영할 때라던지 디바이스의 특정 정보를 게임으로 넘겨줄 때 등등이 그렇겠네요.

반대로  C/C++ 에서 Java로의 예는 어떤 외부 광고 모듈이 Android용 Java와 iOS용 Objective-C로 만들어져 있을 때 

cocos2d-x가 그렇듯 개발중인 프로젝트에 중간에  C/C++ 로 매핑시켜 iOS에서는 Objective-C가 Android는 Java가 호출 되도록 할 수 있겠네요.

자이제 cocos2d-x의 Helloworld 샘플에 JNI를 사용하는 간단한 샘플을 적용해 보겠습니다. 먼저 Java -> Native Code(C/C++) Call 입니다.



cocos2d-x의 HelloWorld 샘플을 이클립스로 열어 src -> org.cocos2dx.application의 ApplicationDemo.java를 엽니다.



public class ApplicationDemo extends Cocos2dxActivity{
 private Cocos2dxGLSurfaceView mGLView;
 protected void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  if (detectOpenGLES20()) {
...
...
            // Set framelayout as the content view
   setContentView(framelayout);
   ///< native code call
   nativeCppFunc();
        }
...
}
///< native code func 선언
private native void nativeCppFunc();



ApplicationDemo의 onCreate에 native code를 call해주는 것을 맨 마지막에 넣어주고 native code func 정의를 추가합니다. 
다음으로 Classes\HelloWorldScene.cpp를 수정해야합니다.


///< JniHelper를 추가
#include "platform/android/jni/JniHelper.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
native code 함수 정의는
리턴 타입 Java_packagename_classname_functionName(JNIEnv* env, jObject thisObj, parameters)로 한다.
Java, packagename, classpath, functionName간에는 _ 로 구분지어준다.
functionName은 Java와 같아야한다.
*/
void Java_org_cocos2dx_application_ApplicationDemo_nativeCppFunc(JNIEnv* env, jobject thisObj)
{
  CCMessageBox( "Call me From Java", "JNI Test" );
}
#ifdef __cplusplus
}
#endif



위와 같이 c++코드를 추가합니다. 단순히 MessageBox를 호출하는게 다입니다. 이제 Cygwin으로 build_native하고 이클립스에서 빌드 후 실행하면,




잘 작동하네요. 제 폰에서 구동한 스샷입니다.

여기서 잠깐 오류상황 하나 나왔던 것에 대해 끄적거려봅니다.


09-26 11:10:20.964: W/dalvikvm(12593): No implementation found for native Lorg/cocos2dx/application/ApplicationDemo;.nativeCppFunc (II)I
09-26 11:10:20.964: D/AndroidRuntime(12593): Shutting down VM
09-26 11:10:20.964: W/dalvikvm(12593): threadid=1: thread exiting with uncaught exception (group=0x40a6b1f8)
09-26 11:10:21.129: E/AndroidRuntime(12593): FATAL EXCEPTION: main
09-26 11:10:21.129: E/AndroidRuntime(12593): java.lang.UnsatisfiedLinkError: nativeCppFunc



구글링을 해보니 java.lang.UnsatisfiedLinkError 는 보통 native library path가 정상적으로 설정되지 않아 파일을 못 찾았을 때 나타나는 예외라고 하더군요. 제 경우는 첨 JNI를 접하다보니 사용하는데 있어 코드상 제대로 bind를 못해줘서 오류가 발생한 것입니다. 



 void Java_com_org_cocos2dx_application_ApplicationDemo_nativeCppFunc(JNIEnv* env, jobject thisObj)



처음에는 이렇게 해줬는데 com을 빼줘야 하더군요.

 void Java_org_cocos2dx_application_ApplicationDemo_nativeCppFunc(JNIEnv* env, jobject thisObj)


이렇게 말이죠.


이제 Native Code(C/C++) -> Java Call 입니다.


public static void JavaJniTestFunc()
{
    Log.d("JNITest", "Success Java Func Call!");
}



ApplicationDemo.java에 간단히 로그를 남기는 static 함수를 추가합니다.


void HelloWorld::menuCloseCallback(CCObject* pSender)
{
  /*
    CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
*/
    JniMethodInfo t;
/**
JniHelper를 통해 org/cocos2dx/application/에 있는 ApplicationDemo class의 JavaJniTestFunc함수 정보를 가져온다.
*/
    if (JniHelper::getStaticMethodInfo(t
        , "org/cocos2dx/application/ApplicationDemo"
        , "JavaJniTestFunc"
        , "()V"))
    {
///< 함수 호출
        t.env->CallStaticVoidMethod(t.classID, t.methodID);
///< Release
        t.env->DeleteLocalRef(t.classID);
    }
}



HelloWorldScene.cpp에서 close버튼 처리부분을 주석으로 막고 Jni관련 소스를 추가합니다. 이제 close버튼을 누르면 Java소스가 호출이 됩니다.




이렇게 로그가 찍힌 걸 볼 수 있습니다.

좀 더 자세한 것은 cocos2d-x How to use jni를 참고하셔도 되고 cocos2d-x가 구현한 소스를 참고하셔도 됩니다. 


소스는 cocos2dx\platform\android\jni 의 jni파일과 cocos2dx\platform\android\java\src_common\org\cocos2dx\lib 그와 관련된 

cocos2d-x의 java파일을 참고하시면 좋습니다. 아니면 CocosDenshion에 있는 SimpleAudioEngine과 그와 관련 된 Android파일을 참고하셔도 됩니다.

간단하게 cocos2d-x HelloWorld 샘플에 JNI를 붙여봤습니다. 

이제 저는 처음에 언급했던 C++ -> Java에 대한 것을 지금 진행중인 Framework에 붙여보는 일이 남아있네요. 

진행하면서 나오는 이슈들도 계속해서 정리해보겠습니다.