Cocos2dx之通過JNI實現c/c++和Android的java層函式互調
親測成功!
呼叫的流程是:
1、啟動app的時候在onCreate方法裡呼叫的C++裡的setPackageName方法,控制檯輸出相應包名資訊,
2、點選關閉按鈕的時候,在menuCloseCallback先判斷是否為android平臺,是的話就呼叫java裡的showTipDialog方法,
3、彈出對話方塊是點選OK按鈕又呼叫C++裡的方法關閉app。
原文出處:http://codingnow.cn/cocos2d-x/992.html
本文主要實現兩個功能:
(1)通過Android sdk的API得到應用程式的包名(PackageName),然後傳遞給c++層函式。
(2)通過c++函式呼叫Android的java層函式,顯示一個對話方塊,點選按鈕退出程式。
1. 首先來簡單學習一下JNI的相關知識,我這篇文章中簡單實現了怎麼在Android Java層呼叫c++函式。要想使用JNI,必須得包含標頭檔案,android是使用ndk編譯c/c++的,這裡jni.h檔案位於:\android-ndk-r8b\platforms\android-14\arch-arm\usr\include\jni.h,該檔案定義了所有和JNI相關的資料型別和介面。下面是相關程式碼片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
#
include <inttypes.h> /* C99 */ typedef uint8_t
jboolean; /*
unsigned 8 bits */ typedef int8_t
jbyte; /*
signed 8 bits */ typedef uint16_t
jchar; /*
unsigned 16 bits */ typedef int16_t
jshort; /*
signed 16 bits */ typedef int32_t
jint; /*
signed 32 bits */ typedef int64_t
jlong; /*
signed 64 bits */ typedef float
jfloat; /*
32-bit IEEE 754 */ typedef double
jdouble; /*
64-bit IEEE 754 */ #else typedef unsigned
char jboolean;
/*
unsigned 8 bits */ typedef signed
char
jbyte; /*
signed 8 bits */ typedef unsigned
short jchar;
/*
unsigned 16 bits */ typedef short
jshort; /*
signed 16 bits */ typedef int
jint; /*
signed 32 bits */ typedef long
long
jlong; /*
signed 64 bits */ typedef float
jfloat; /*
32-bit IEEE 754 */ typedef double
jdouble; /*
64-bit IEEE 754 */ #endif /*
"cardinal indices and sizes" */ typedef jint
jsize; #ifdef
__cplusplus /* *
Reference types, in C++ */ class _jobject
{}; class _jclass
: public _jobject
{}; class _jstring
: public _jobject
{}; class _jarray
: public _jobject
{}; class _jobjectArray
: public _jarray
{}; class _jbooleanArray
: public _jarray
{}; class _jbyteArray
: public _jarray
{}; class _jcharArray
: public _jarray
{}; class _jshortArray
: public _jarray
{}; class _jintArray
: public _jarray
{}; class _jlongArray
: public _jarray
{}; class _jfloatArray
: public _jarray
{}; class _jdoubleArray
: public _jarray
{}; class _jthrowable
: public _jobject
{}; typedef _jobject*
jobject; typedef _jclass*
jclass; typedef _jstring*
jstring; typedef _jarray*
jarray; typedef _jobjectArray*
jobjectArray; typedef _jbooleanArray*
jbooleanArray; typedef _jbyteArray*
jbyteArray; typedef _jcharArray*
jcharArray; typedef _jshortArray*
jshortArray; typedef _jintArray*
jintArray; typedef _jlongArray*
jlongArray; typedef _jfloatArray*
jfloatArray; typedef _jdoubleArray*
jdoubleArray; typedef _jthrowable*
jthrowable; typedef _jobject*
jweak; #else
/* not __cplusplus */ /* *
Reference types, in C. */ typedef void *
jobject; typedef jobject
jclass; typedef jobject
jstring; typedef jobject
jarray; typedef jarray
jobjectArray; typedef jarray
jbooleanArray; typedef jarray
jbyteArray; typedef jarray
jcharArray; typedef jarray
jshortArray; typedef jarray
jintArray; typedef jarray
jlongArray; typedef jarray
jfloatArray; typedef jarray
jdoubleArray; typedef jobject
jthrowable; typedef jobject
jweak; #endif
/* not __cplusplus */ |
我們經常用到的是JNIEnv*,它是一個c結構體,封裝了許多常用的函式,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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,定義了一些常用的介面,該檔案位於cocos2dx/platform/android/jni目錄下。下面看看JniHelper.h原始碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
typedef struct
JniMethodInfo_ { JNIEnv
* env; jclass
classID; jmethodID
methodID; }
JniMethodInfo; class CC_DLL
JniHelper { public : static JavaVM*
getJavaVM(); static void
setJavaVM(JavaVM *javaVM); static const
char *
getExternalAssetPath(); static void
setExternalAssetPath( const char *
externalAssetPath); static jclass
getClassID( const char
*className, JNIEnv *env=0); static bool
getStaticMethodInfo(JniMethodInfo &methodinfo, const char
*className, const char
*methodName, const char
*paramCode); static bool
getMethodInfo(JniMethodInfo &methodinfo, const char
*className, const char
*methodName, const char
*paramCode); static std::string
jstring2string(jstring str); private : static JavaVM
*m_psJavaVM; static std::string
m_externalAssetPath; }; |
下面來解釋JniHelper的兩個常用函式:
(1)getStaticMethodInfo
用來判斷Java的類靜態函式是否存在,並初始化結構體JniMethodInfo,該結構體封裝了JNIEnv*和java.lang.Class物件、函式ID。這樣就可以使用JNIEnv*呼叫 CallStaticXXXMethod(jclass clazz, jmethodID methodID, …)和 CallXXXMethod(jobject obj, jmethodID methodID, …)等常用函式(XXX替換為函式返回值型別,如:Void,Int等)。
第一個引數為JniMethodInfo,第二個引數是類的絕對路徑,第三個引數是函式名,第四個引數是函式簽名(引數和返回型別),示例程式碼如下:
1
2
3
4
|
if (JniHelper::getStaticMethodInfo(t,
CLASS_NAME, "showTipDialog" , "(Ljava/lang/String;Ljava/lang/String;)V" )) { //... } |
關於型別簽名,請對照下圖:
(2)getMethodInfo
該函式與getStaticMethodInfo類似,用於Java類的非靜態函式。
2. 下面開始實現文章開頭所述的兩個功能,本文是在cocos2d-x 2.0版本 自適應螢幕解析度demo的基礎上新增的。
(1)利用cocos2d-x建立一個Android工程,名為JniTest,包名為com.alexzhou.jni,此時該包下會自動生成一個JniTest.java檔案。
(2)首先來實現把應用程式的包名傳遞給c++函式,在包下建立JniTestHelper.java,該類封裝了給c++呼叫的函式,新增如下程式碼:
1
2
3
4
5
6
7
8
|
private static
Handler mHandler; public static
void
init(Handler handler) { JniTestHelper.mHandler
= handler; } public static
native
void
setPackageName(String packageName); |
(3)開啟JniTest.java,在onCreate函式中新增下面的程式碼:
1
2
3
4
5
|
protected void
onCreate(Bundle savedInstanceState){ super .onCreate(savedInstanceState); JniTestHelper.init(mHandler); JniTestHelper.setPackageName( this .getPackageName()); } |
(4)java層的程式碼已經完成了,下面來編寫jni層程式碼,在/jni/hellocpp/下建立test.h和test.cpp檔案,test.h檔案暫時不新增任何函式,程式碼如下:
test.h
1
2
3
4
|
#ifndef
TEST_H #define
TEST_H #endif |
test.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include
"cocos2d.h" #include
<jni.h> #include
"platform/android/jni/JniHelper.h" #include
"test.h" #include
"JniTest.h" #define
CLASS_NAME "com/alexzhou/jni/JniTestHelper" using namespace
cocos2d; extern "C" { void Java_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv
*env, jobject thiz, jstring packageName) { const char
*pkgName = env->GetStringUTFChars(packageName, NULL); setPackageName(pkgName); env->ReleaseStringUTFChars(packageName,
pkgName); } } |
必須加上extern “C”,宣告以c語言的方式進行編譯,因為c++和c在編譯時生成的函式簽名不一樣,可以在網上查詢相關資料,不然執行的時候會出現連結錯誤。
(5)現在編寫c++函式,在Classes目錄下建立JniTest.h,程式碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#ifndef
JNI_TEST_H #define
JNI_TEST_H #include
"cocos2d.h" using namespace
cocos2d; void setPackageName( const char
*packageName) { CCLog( "packageName:
%s" ,
packageName); } #endif |
(6)修改jni/Android.mk檔案的LOCAL_SRC_FILES值 ,內容如下:
1
2
|
LOCAL_SRC_FILES
:= hellocpp /main .cpp
\ hellocpp /test .cpp |
(7)編譯執行,因為我是使用cygwin編譯的,而且Android專案不在cocos2d-x的根目錄下,所以需要修改build_native.sh,修改COCOS2DX_ROOT和NDK_MODULE_PATH的值,把當前cocos2d-x專案的路徑新增到NDK_MODULE_PATH,修改後的值:
1
2
3
|
COCOS2DX_ROOT= "/cygdrive/e/cocos2d-x/cocos2d-2.0-x-2.0.4" "NDK_MODULE_PATH=${COCOS2DX_ROOT}:${COCOS2DX_ROOT}/cocos2dx/platform/third_party/android/prebuilt:${APP_ROOT}" |
執行結果:
(8)現在來實現通過c++函式呼叫java層函式,顯示一個對話方塊。在JniTestHelper.java新增如下程式碼:
1
2
3
4
5
6
7
8
9
10
11
12
|
public static
native
void
exitApp(); private static
void
showTipDialog( final String
title, final String
text) { Message
msg = mHandler.obtainMessage(); msg.what
= JniTest.SHOW_DIALOG; DialogMessage
dm = new DialogMessage(); dm.title
= title; dm.msg
= text; msg.obj
= dm; msg.sendToTarget(); } |
(9)建立一個DialogMessage.java,封裝dialog要顯示的資料。
1
2
3
4
5
6
7
8
9
10
11
|
/** author:alexzhou email
:zhoujiangbohai@163.com date
:2012-12-14 **/ public class
DialogMessage { public String
title; public String
msg; } |
(10) 修改JniTest.java,新增顯示對話方塊的函式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public static
final
int
SHOW_DIALOG = 0x0001 ; private Handler
mHandler = new Handler() { @Override public void
handleMessage(Message msg) { switch (msg.what) { case SHOW_DIALOG: DialogMessage
dm = (DialogMessage)msg.obj; new AlertDialog.Builder(JniTest. this ) .setTitle(dm.title) .setMessage(dm.msg).setNegativeButton( "cancle" , new DialogInterface.OnClickListener()
{ @Override public void
onClick(DialogInterface dialog, int which)
{ dialog.dismiss(); } }) .setPositiveButton( "Ok" , new DialogInterface.OnClickListener()
{ @Override public void
onClick(DialogInterface dialog, int which)
{ dialog.dismiss(); JniTestHelper.exitApp(); } }) .create().show(); break ; } } }; |
(11)在test.h和test.cpp中新增顯示對話方塊的介面:
test.h
1
2
3
4
|
extern "C" { void showTipDialog( const char
*title, const char
*msg); } |
test.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
extern "C" { void showTipDialog( const char
*title, const char
*msg) { JniMethodInfo
t; 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); } } void Java_com_alexzhou_jni_JniTestHelper_setPackageName(JNIEnv
*env, jobject thiz, jstring packageName) { const char
*pkgName = env->GetStringUTFChars(packageName, NULL); setPackageName(pkgName); env->ReleaseStringUTFChars(packageName,
pkgName); } void Java_com_alexzhou_jni_JniTestHelper_exitApp(JNIEnv
*env, jobject thiz) { exitApp(); } } |
(12) 修改Classes目錄下的JniTest.h,新增程式碼:
1
2
3
4
|
void exitApp() { CCDirector::sharedDirector()->end(); } |
(13)到此為止,所有程式碼都已經完成了,原始碼地址:http://download.csdn.net/detail/zhoujianghai/4890792
相關文章
- android實現app通過jni呼叫C/C++方法AndroidAPPC++
- C、C++、Java?Java Native Interface(JNI)特輯——C反射java函式C++Java反射函式
- C++庫封裝JNI介面——實現java呼叫c++C++封裝Java
- C++之函式過載C++函式
- C/C++—— C++中函式重寫和函式過載C++函式
- Android 通過JNI實現守護程式Android
- Android JNI實現Java與C/C++互相呼叫,以及so庫的生成和呼叫(JNI方式呼叫美圖秀秀so)AndroidJavaC++
- c語言中通過函式指標實現函式過載C語言函式指標
- 通過JNI對C++進行封裝C++封裝
- android層java如何呼叫cocos2dx c++程式碼 步驟AndroidJavaC++
- Android Binder實現示例(C/C++層)AndroidC++
- C中的基本輸入輸出函式(Android之JNI)函式Android
- C++ 函式過載和模板C++函式
- 通過JVMTI和JNI對JAVA加密JVMJava加密
- C++的函式過載C++函式
- Effective c++(筆記)----類與函式之實現C++筆記函式
- 開心檔之C++ 過載運算子和過載函式C++函式
- C++ 過載運算子和過載函式C++函式
- C++函式過載C++函式
- C++的函式的過載C++函式
- java呼叫c++動態庫之jni呼叫JavaC++
- Music Studio專案心得--JNI實現C++呼叫JAVAC++Java
- C++ 初始化函式的實現C++函式
- C++的函式和模板函式 (轉)C++函式
- c++字串查詢函式實現C++字串函式
- C++之memset函式C++函式
- Android C++層使用Binder通訊的方法AndroidC++
- android使用JNI呼叫C,C++程式AndroidC++
- C++ 函式過載,函式模板和函式模板過載,選擇哪一個?C++函式
- C++ 逆向之 move 函式C++函式
- C++ 字串截斷的實現(基礎函式)C++字串函式
- 深度解讀《深度探索C++物件模型》之C++虛擬函式實現分析(一)C++物件模型函式
- 深度解讀《深度探索C++物件模型》之C++虛擬函式實現分析(二)C++物件模型函式
- 深度解讀《深度探索C++物件模型》之C++虛擬函式實現分析(三)C++物件模型函式
- C/C++的mem函式和strcopy函式的區別和應用C++函式
- Android Studio NDK開發:JNI呼叫Java函式AndroidJava函式
- C的指標(Android之JNI)指標Android
- 在Delphi與C++之間實現函式與物件共享 (轉)C++函式物件