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

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에 붙여보는 일이 남아있네요. 진행하면서 나오는 이슈들도 계속해서 정리해보겠습니다.

cocos2d-x JNI작업시 Activity UI Thread와 GLSurfaceView의 GLThread간 ThreadSafe하게 메세지 통신하기

댓글

  1. 안녕하세요~
    저번에 답변감사드립니다 ^^
    게시물보러 매일 들르곤합니다 ㅎㅎ
    그런데 Jni에서 여쭤볼게
    Java -> c++ 호출한뒤에
    void Java_xxx_xxx_Xxx(JNIEnv* env, jobject thisObj)
    {
    CCMessageBox( "", "" );
    }
    이렇게 받아오게되면 위 함수내에서는 cocos2d를상속?이 안되서 인지 cocos2d기능을쓸수가없어서 addChild등이 되지않네요 cocos2d를 상속받거나 현재 Running중인 Scene의 클래스를 가져오면될거같은데 C++ 실력이 얕아서 잘되지않네요 ㅜㅜ

    답글삭제
    답글
    1. 안녕하세요 ^^ 찾아주셔서 감사합니다~
      음.. 일단 아무래도 jni c++ 딴은 말씀하신데로 게임쪽과는 무관하게 좀 동떨어져 있는 부분이긴 하죠.
      자바에서 온 메세지?를 핸들링 처리하는 수준이니까요.
      필요한 cocos2d관련 h파일( cocos2d.h 라던지 )과 만드셨던 Scene이나 필요하신 class 객체등을 include하신 후 싱글턴 객체나 전역 객체 같은 것을 가져와서 사용하셔야 겠죠.

      현재 run 중인 scene는 CCDirector을 통해 가져올 수 있어요.

      ///jni 처리 cpp 파일

      #include "cocos2d.h"

      void Java_xxx ...
      {
      CCScene* pRunScene = CCDirector::sharedDirector()->getRunningScene();
      }

      위와 같이 하시면 잘 될꺼에요.

      즐건 하루 되세요~

      삭제
    2. 도움이 많이 되었습니다
      알려주신대로 열심히하고있어요!

      삭제
    3. ^^ 이 포스팅에 있는 jni 내용은 샘플수준이고 맨 밑에 있는 GLThread부분을 더 보셔야 할꺼에요. 나중에 쓰레드 문제가 생기거든요.
      열 코딩 하세요~

      삭제
    4. 저도 안상학 님과 똑같은 상황인데요 ㅠ 아래의 경우들 처럼 하니까 이러한 에러가 나더라구요;;;
      Fatal signal 11 (SIGSEGV) at 0x0000001f (code=1), thread 29037 (Thread-18450)

      경우 1 >

      #include "cocos2d.h"
      void Java_xxx ...
      {
      CCScene* pRunScene = CCDirector::sharedDirector()->getRunningScene();
      CCDirector::sharedDirector()->popScene();
      }


      경우 2 >

      #include "cocos2d.h"
      void Java_xxx ...
      {
      CCScene *pScene = HelloWorld::scene();
      CCScene* pRunScene = CCDirector::sharedDirector()->getRunningScene();
      CCDirector::sharedDirector()->pushScene(pScene);
      }

      제가 하고자 하는것은java에서 c++함수를 호출했을때 HellowWorld의 testInApp() 메서드를 실행시키고 싶은것입니다 ㅠ 아니면 cocos2d 메인 코드에 있는 전역변수의 값을 변경하고 싶은것입니다 ㅠ

      삭제
    5. 아 여기도 글을 쓰셨네요. 이것도 해결이 되신건가요?
      http://westwoodforever.blogspot.kr/2012/10/cocos2d-x-jni-activity-ui-thread.html
      이 링크를 확인해보시면 되는데..

      삭제
  2. 인앱구현때문에 어떻게하는지 알고싶었는데 깔끔한 정리 감사합니다^^

    근데 사용 예 들어주신거에서 'Java에서 C/C++ 로' 이말이 자바에서 C/C++함수 콜 한다는 말씀이죠? ㅎㅎ

    답글삭제
    답글
    1. 네. 자바에서 native c++ 호출하는 것이죠~
      즐 코딩 하세요 ^^

      삭제

댓글 쓰기

이 블로그의 인기 게시물

라즈베리파이 라즈비안 한글 입력하기 - IBus

'xxx.exe' 프로그램을 시작할 수 없습니다. 지정된 파일을 찾을 수 없습니다.

라즈베리파이 라즈비안 한글 깨짐 수정하기