程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Cocos2d-x3.3RC0通過JNI實現Java與C++互調

Cocos2d-x3.3RC0通過JNI實現Java與C++互調

編輯:C++入門知識

Cocos2d-x3.3RC0通過JNI實現Java與C++互調


一、JNI

JNI(Java Native Interface):Java的本地調用。本文通過JNI在Cocos2d-x3.3RC0中完成Java與C++的互調。具體實現以下兩個功
能:(1)通過Android sdk的API得到應用程序的包名,並傳遞給C++層函數。(2)通過C++函數調用Android的Java層函數,顯示一個對話框。點擊按鈕退出程序。
詳細知識見:http://blog.csdn.net/yuxikuo_1/article/details/39577257。其中最重要的是JNIEnv,這是一個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函數,返回一個對象  
    }  
    //判斷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、創建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;

void setPackageName(const char* packageName)//從Java層傳過來的包名在此處打印出來
{
    log("packageName = %s",packageName);
}

void exitApp()//Java層調用C++層的該函數,關閉程序。
{
    Director::getInstance()->end();
}

然後在HelloWorldScene.cpp中包含如下頭文件,並在menuCloseCallback中添加如下代碼:
//頭文件包含,判斷平台
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "../proj.android/jni/hellocpp/test.h"//一定是相對路徑
#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。代碼如下:一定包含extern "C"
//test.h
#ifndef TEST_H_
#define TEST_H_
extern "C"
{
	void showTipDialog(const char* title,const char* msg);
}
#endif
test.cpp代碼如下:
//test.cpp
#include "test.h"
#include "cocos2d.h"
#include "platform/android/jni/JniHelper.h"//引擎提供的JniHelper類
#include "../../../Classes/JniTest.h"//相對路徑,上一步在Xcode中創建的類
#include 

#define CLASS_NAME "org/cocos2dx/cpp/JniTestHelper"//這步的路徑即是上面紅線的路徑,很重要
using namespace cocos2d;
extern "C"
{	//下面的函數通過Jni調用Java層的函數。
	void showTipDialog(const char* title,const char* msg)//在HelloWorldScene中調用該處的此函數,此函數在通過Jni傳到Java層
	{						//調用Java層JniTestHelper.java中的showTipDialog函數
		JniMethodInfo t;
		if(JniHelper::getStaticMethodInfo(t,CLASS_NAME,"showTipDialog","(Ljava/lang/String;Ljava/lang/String;)V"))//該函數的意思就是,尋找org/cocos2dx/cpp/JniTestHelper類中有無靜態函數showTipDialog,暫時還沒有放出JniTestHelper的代碼,稍後。
		{
			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);
		}
	}//以下兩個函數,是通過Jni調用C++層的setPackageName和exitApp,Java層的兩個函數在JniTestHelper中定義,參數通過JNIEnv傳入
	void Java_org_cocos2dx_cpp_JniTestHelper_setPackageName(JNIEnv* env,jobject thiz,jstring packageName)
	{
		const char* pkgName = env->GetStringUTFChars(packageName,NULL);
		setPackageName(pkgName);//C++層代碼
		env->ReleaseStringUTFChars(packageName,pkgName);
	}
	void Java_org_cocos2dx_cpp_JniTestHelper_exitApp(JNIEnv* env,jobject thiz)
	{
		exitApp();//C++層代碼
	}
}

5、Java層函數

在ADT工程目錄src/org.cocos2dx.cpp的目錄下添加java類,JniTestHelper.java
1)JniTestHelper.java代碼如下:
package org.cocos2dx.cpp;
import org.cocos2dx.lib.Cocos2dxHandler.DialogMessage;
import android.os.Handler;
import android.os.Message;

public class JniTestHelper {
	private static Handler mHandler;//Java的Handler傳遞消息
	public static void init(Handler handler) {
		JniTestHelper.mHandler = handler;
	}
	
	public static native void setPackageName(String packageName);//聲明兩個靜態nativa函數,在Jni中test.cpp中定義,調用C++層的對應函數。
	
	public static native void exitApp();
	
	private static void showTipDialog(final String title,final String text)
	{
		Message msg = mHandler.obtainMessage();//接受消息
		msg.what = AppActivity.SHOW_DIALOG;
		DialogMessage dm = new DialogMessage(title, text);//重點是這一步,之前的教程自己定的數據結構,而新版本的Cocos的
		dm.titile = title;				//Jni庫為我們提供了DialogMessage這個數據結構類。所以不用自定義
		dm.message = text;
		msg.obj = dm;
		msg.sendToTarget();
	}
}
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);//將下面定義的Handler對象,初始化JniTestHelper中的Handler對象。
		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();
					}
				})
				.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版本,不改無所謂也。

6、工程總目錄



\

三、編譯運行

如出現問題可參考 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。
源碼
說了這麼多,沒有源碼那不是坑爹麼。源碼連接:http://pan.baidu.com/s/1jGn80QE

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved