Cocos2d-x3.3RC0通過JNI實現Java與C++互調
來源:程序員人生 發布時間:2014-11-06 09:44:16 閱讀次數:2792次
1、JNI
JNI(Java Native Interface):Java的本地調用。本文通過JNI在Cocos2d-x3.3RC0中完成Java與C++的互調。具體實現以下兩個功
能:(1)通過Android sdk的API得到利用程序的包名,并傳遞給C++層函數。(2)通過C++函數調用Android的Java層函數,顯示1個對話框。點擊按鈕退出程序。
詳細知識見:http://blog.csdn.net/yuxikuo_1/article/details/39577257。其中最重要的是JNIEnv,這是1個C結構體。封裝了許多
經常使用函數:具體以下:
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion()
{ return functions->GetVersion(this); }
jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
// 這里省略其他函數...
}
Cocos2d-x對jni的操作進行了封裝,提供JniHelper類解決Java與C++的通訊。
下面介紹兩個經常使用的函數:
1、getStaticMethodInfo:
用來判斷java類中靜態函數是不是存在,初始化結構體JniMethodInfo。該結構體封裝了JNIEnv*和java.lang.Class、函數ID。這樣可使用JNIEnv*調用CallStaticXXXMethod(jclass clazz,jmethodID methodID,...)和CallXXXMethod(jobject obj,jmethodID
methodID,...)等經常使用函數,其中XXX代表函數返回值類型,如void、int等。以下代碼:參數1:JniMethodInfo,參數2:類的絕對路徑,該路徑為:proj.android/src/下的目錄,例如引擎模板工程下的路徑為:src/org/cocos2dx/cpp/XXX。XXX為cpp下的java文件。記住路徑中不用加.java后綴,由于路徑使用的是類名。參數3:函數名,參數4:函數簽名,具體規則見3類型簽名
JniMethodInfo info;
bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/AppActivity","getObj","()Ljava/lang/Object;");
jobject jobj;
if(ret)
{
log("call void getObj() succeed");
jobj = info.env->CallStaticObjectMethod(info.classID,info.methodID);
}
bool re = JniHelper::getMethodInfo(info,"org/cocos2dx/cpp/AppActivity","func1","()V");
if(re)
{
log("call func1 succeed");
info.env->CallVoidMethod(jobj,info.methodID);
}
2、getMethodInfo:用于調用Java類的非靜態函數
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo info;
//判斷org/cocos2dx/cpp/AppActivity.java中是不是存在getObj靜態函數
bool ret = JniHelper::getStaticMethodInfo(info,"org/cocos2dx/cpp/AppActivity","getObj","()Ljava/lang/Object;");
jobject jobj;//用于寄存返回的對象
if(ret)
{
log("call void getObj() succeed");
jobj = info.env->CallStaticObjectMethod(info.classID,info.methodID);//調用getObj函數,返回1個對象
}
//判斷org/cocos2dx/cpp/AppActivity.java中是不是存在func1非靜態函數
bool re = JniHelper::getMethodInfo(info,"org/cocos2dx/cpp/AppActivity","func1","()V");
if(re)
{
log("call func1 succeed");
info.env->CallVoidMethod(jobj,info.methodID);//通過返回的對象調用非靜態函數
}
#endif
3、類型簽名
類型簽名 |
Java類型 |
Z |
boolean |
B |
byte |
C |
char |
S |
short |
I |
int |
J |
long |
F |
float |
D |
double |
L full-qualified-class; |
完全限定的類 |
[ type |
type[ ] |
(arg-types) ret-type |
方法類型 |
如java方法:long f(int n,String s,int[] arr); 類型簽名為:(ILjava/lang/String;[I)J。注意L后的分號,[是半開的,要與類型簽名完全1致。
2、具體步驟
1、創建Cocos2d-x3.3RC0工程
這個不做過量介紹,既然研究到Jni了,相比都不是太菜鳥了。
2、ADT與XCode分別導入工程
3、Xcode的Class目錄下添加JniTest類
JniTest.h代碼以下:JniTest.cpp暫時沒有代碼
#ifndef __JniDemo__JniTest__
#define __JniDemo__JniTest__
#include "cocos2d.h"
USING_NS_CC;
//定義兩個C++方法,在Jni的test.h中被Java方法調用。
//該方法被Java_org_cocos2dx_cpp_JniTestHelper_setPackageName調用
//定義org/cocos2dx/cpp/JniTestHelper類中的setPackageName方法
void setPackageName(const char* packageName)
{
log("packageName = %s",packageName);
}
//該方法被Java_org_cocos2dx_cpp_JniTestHelper_exitApp調用
//定義org/cocos2dx/cpp/JniTestHelper類中得exitApp函數
void exitApp()
{
Director::getInstance()->end();
}
#endif /* defined(__JniDemo__JniTest__) */
然后在HelloWorldScene.cpp中包括以下頭文件,并在menuCloseCallback中添加以下代碼:
//頭文件包括,判斷平臺
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "../proj.android/jni/hellocpp/test.h"//1定是相對路徑
#endif
//調用C++調用Java層代碼
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
showTipDialog("exit","Exit,Really Go?");
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
4、Jni層代碼
打開ADT工程目錄下的jni/hellocpp/列表,在hellocpp下添加c++類,test.cpp和test.h。代碼以下:1定包括extern "C"
</pre><p class="p1"><pre name="code" class="cpp">#ifndef TEST_H_
#define TEST_H_
extern "C"
{
//C++調Java的函數接口,該方法在HelloWorldScene中menuCallback函數中使用。
void showTipDialog(const char* title,const char* msg);
}
#endif
test.cpp代碼以下:
#include "test.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"
#include "../../../Classes/JniTest.h"
#include <jni.h>
#define CLASS_NAME "org/cocos2dx/cpp/JniTestHelper"
using namespace cocos2d;
extern "C"
{
//C++調Java的函數,在HelloWorldScene中的menuCallback函數中調用。
void showTipDialog(const char* title,const char* msg)
{
JniMethodInfo t;
//判斷CLASS_NAME的類中是不是存在showTipDialog函數,如果存在,則調用。
if(JniHelper::getStaticMethodInfo(t,CLASS_NAME,"showTipDialog","(Ljava/lang/String;Ljava/lang/String;)V"))
{
jstring jTitle = t.env->NewStringUTF(title);
jstring jMsg = t.env->NewStringUTF(msg);
t.env->CallStaticVoidMethod(t.classID,t.methodID,jTitle,jMsg);
t.env->DeleteLocalRef(jTitle);
t.env->DeleteLocalRef(jMsg);
}
}
//Java調C++函數,Java函數來自org/cocos2dx/cpp/JniTestHelper的java類。同時該函數為JniTestHelper的java類中setPackageName原生方法的定義
void Java_org_cocos2dx_cpp_JniTestHelper_setPackageName(JNIEnv* env,jobject thiz,jstring packageName)
{
const char* pkgName = env->GetStringUTFChars(packageName,NULL);
setPackageName(pkgName);//此處調用的是Class目錄下JniTest類中的函數
env->ReleaseStringUTFChars(packageName,pkgName);
}
//同上,該函數定義了JniTestHelper的java類中的exitApp原生函數。
void Java_org_cocos2dx_cpp_JniTestHelper_exitApp(JNIEnv* env,jobject thiz)
{
exitApp();//此處調用的時Class目錄中JniTest類中的函數。
}
}
5、Java層函數
在ADT工程目錄src/org.cocos2dx.cpp的目錄下添加java類,JniTestHelper.java
1)JniTestHelper.java代碼以下:
#include "test.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"
#include "../../../Classes/JniTest.h"
#include <jni.h>
#define CLASS_NAME "org/cocos2dx/cpp/JniTestHelper"
using namespace cocos2d;
extern "C"
{
//C++調Java的函數,在HelloWorldScene中的menuCallback函數中調用。
void showTipDialog(const char* title,const char* msg)
{
JniMethodInfo t;
//判斷CLASS_NAME的類中是不是存在showTipDialog函數,如果存在,則調用。
if(JniHelper::getStaticMethodInfo(t,CLASS_NAME,"showTipDialog","(Ljava/lang/String;Ljava/lang/String;)V"))
{
jstring jTitle = t.env->NewStringUTF(title);
jstring jMsg = t.env->NewStringUTF(msg);
t.env->CallStaticVoidMethod(t.classID,t.methodID,jTitle,jMsg);
t.env->DeleteLocalRef(jTitle);
t.env->DeleteLocalRef(jMsg);
}
}
//Java調C++函數,Java函數來自org/cocos2dx/cpp/JniTestHelper的java類。同時該函數為JniTestHelper的java類中setPackageName原生方法的定義
void Java_org_cocos2dx_cpp_JniTestHelper_setPackageName(JNIEnv* env,jobject thiz,jstring packageName)
{
const char* pkgName = env->GetStringUTFChars(packageName,NULL);
setPackageName(pkgName);//此處調用的是Class目錄下JniTest類中的函數
env->ReleaseStringUTFChars(packageName,pkgName);
}
//同上,該函數定義了JniTestHelper的java類中的exitApp原生函數。
void Java_org_cocos2dx_cpp_JniTestHelper_exitApp(JNIEnv* env,jobject thiz)
{
exitApp();//此處調用的時Class目錄中JniTest類中的函數。
}
}
2)AppActivity.java代碼
package org.cocos2dx.cpp;
import org.cocos2dx.lib.Cocos2dxActivity;
import org.cocos2dx.lib.Cocos2dxGLSurfaceView;
import org.cocos2dx.lib.Cocos2dxHandler.DialogMessage;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
public class AppActivity extends Cocos2dxActivity{
public static final int SHOW_DIALOG = 0x0001;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
JniTestHelper.init(mHandler);
//調用JniTestHelper的setPackageName函數,setPackageName函數在test.cpp中定義,終究調用C++層的setPackageName方法
JniTestHelper.setPackageName(this.getPackageName());
}
public Cocos2dxGLSurfaceView onCreateView(){
Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
glSurfaceView.setEGLConfigChooser(5,6,5,0,16,8);
return glSurfaceView;
}
static {
System.loadLibrary("cocos2dcpp");
}
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg) {
switch (msg.what)
{
case SHOW_DIALOG:
DialogMessage dm = (DialogMessage)msg.obj;
new AlertDialog.Builder(AppActivity.this)
.setTitle(dm.titile)
.setMessage(dm.message).setNegativeButton("cancle", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
arg0.dismiss();
}
})
.setPositiveButton("Ok",new DialogInterface.OnClickListener(){
public void onClick(DialogInterface arg0, int arg1) {
arg0.dismiss();
JniTestHelper.exitApp();//調用JniTestHelper的exitApp函數,exitApp函數在test.cpp中定義,終究調用C++層的exitApp方法
}
})
.create().show();
break;
}
}
};
}
好了,代碼和注釋基本就結束了,還需要該的是Android.mk文件。代碼以下:
LOCAL_SRC_FILES := hellocpp/main.cpp
hellocpp/test.cpp //將新建的test.cpp類加入mk文件
../../Classes/AppDelegate.cpp
../../Classes/HelloWorldScene.cpp
之前介紹過萬能mk文件生成方法,詳見http://blog.csdn.net/yuxikuo_1/article/details/39552431。為了減少出問題的概率,建議改高AndroidManifest中的SDK版本,不改無所謂也。
如出現問題可參考
1)http://blog.csdn.net/yuxikuo_1/article/details/39654499
2)http://blog.csdn.net/yuxikuo_1/article/details/39552639
3)http://blog.csdn.net/yuxikuo_1/article/details/39671733
注:環境Mac XCode6 ADT22.2.1 Cocos2d-x3.3RC0 紅米Note。
4、源碼
說了這么多,沒有源碼那不是坑爹么。源碼連接:http://pan.baidu.com/s/1jGn80QE
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈