文章摘要: 本文主要实现两个功能: (1)通过Android sdk的API得到应用程序的包名(PackageName),然后传递给c++层函数。 (2)通过c++函数调用Android的java层函数,显示一个对话框,点击按钮退出程序。 1. 首先来简单学习一下JNI的相关知识,我这篇文章中简单实现了怎么在Android Java层调用c++函数。要想使用JNI,必须得…
 
 

本文主要实现两个功能:
(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/kaitiren/6256205

cocos2d-x 通过JNI实现c/c++和Android的java层函数互调的更多相关文章

  1. Cocos2d-x3.3RC0通过JNI调用Android的Java层URI代码发送短信

    1.Jni不在赘述.翻看前面博客 2.直接上代码 1)Java层,直接加在AppActivity.java中 public class AppActivity extends Cocos2dxActi ...

  2. JNI的又一替代者—使用JNR访问Java外部函数接口(jnr-ffi)

    1. JNR简单介绍 继上文“JNI的替代者—使用JNA访问Java外部函数接口”,我们知道JNI越来越不受欢迎,JNI是编写Java本地方法以及将Java虚拟机嵌入本地应用程序的标准编程接口.它管理 ...

  3. cocos2d 中使用jni C++ 调用 Java 方法

    1.简单数据类型样例 如果我们Java中有这么一个open的静态方法,它没有參数,有一个int的返回值.怎么在C++中调用它呢? package cb.CbCCBLE; public class Cb ...

  4. cocos2d 中使用jni Java 调用 C++ 方法

    1.首先是LoadLibrary cocos2d中的C++代码会编译成一个.so文件.放在安卓文件夹下的libs/armeabi 下,然后java会load进来,这步我们不用做了,由于cocos2d已 ...

  5. Android JNI 由C/C++本地代码向Java层传递数据

    最近做的Android项目需要调用C代码,进行串口通信及与硬件设备通信,因此要用到JNI,其中本地代码需要向Java层返回三个参数,分别为 参数一:int型: 参数二: 通信指令,本地代码中为unsi ...

  6. [Android Webkit]JNI基础及Java层与C++层的交互

    1. JNI 注册 1.1. JNI的基础结构       JAVA == JNI == Native Code      JNI(Java Native Interface)是Java与Native ...

  7. Android(java)学习笔记257:JNI之helloword案例(利用NDK工具)

    1.逻辑思路过程图: 2.下面通过一个HelloWorld案例来说明一下JNI利用NDK开发过程(步骤) 分析:我们在Win7系统下编译的C语言代码,我们知道C语言依赖操作系统,不能跨平台,所以我们要 ...

  8. JNI的替代者—使用JNA访问Java外部功能接口

    摘自:http://www.cnblogs.com/lanxuezaipiao/p/3635556.html JNI的替代者-使用JNA访问Java外部功能接口 1. JNA简单介绍 先说JNI(Ja ...

  9. 深入理解JNI(《深入理解android》(author : 邓凡平)读书札记)

    JNI的技术特点: java能够调用native代码. native代码能够调用java代码.   JNI的技术考虑: 实现java代码的平台无关型. java语言发展初期使用C和C++代码,避免重复 ...

随机推荐

  1. TerminateThread函数学习

    终结一个线程. BOOL WINAPI TerminateThread( _Inout_  HANDLE hThread, _In_     DWORD dwExitCode ); Parameter ...

  2. 笔记-Nodejs中的核心API之Events

    最近正在学习Node,在图书馆借了基本关于Node的书,同时在网上查阅资料,颇有收获,但是整体感觉对Node的理解还是停留在一个很模棱两可的状态.比如Node中的模块,平时练习就接触到那么几个,其他的 ...

  3. Javaweb统计在线人数的小栗子

    最近在学习Javaweb相关的内容(不黑不吹之前对web开发零基础),下面通过一个统计在线人数的小栗子讲讲Servlet监听器吧 开发环境 eclipse  tomcat 7 先说说这个小栗子的构思: ...

  4. BZOJ 2337: [HNOI2011]XOR和路径( 高斯消元 )

    一位一位考虑异或结果, f(x)表示x->n异或值为1的概率, 列出式子然后高斯消元就行了 --------------------------------------------------- ...

  5. BZOJ 1005: [HNOI2008]明明的烦恼( 组合数学 + 高精度 )

    首先要知道一种prufer数列的东西...一个prufer数列和一颗树对应..然后树上一个点的度数-1是这个点在prufer数列中出现次数..这样就转成一个排列组合的问题了.算个可重集的排列数和组合数 ...

  6. HDU3535-AreYouBusy

    描述: As having become a junior, xiaoA recognizes that there is not much time for her to AC problems, ...

  7. poj3358 Period of an Infinite Binary Expansion 数论有难度

    这道题目感觉好难,根本就是无从下手的感觉,尝试了以前的所有方法,都没有思路,毫无进展,参考了一下别人的思路,感觉学到了新的知识 接下来开始分析 观察1/10这组数据,按照二进制转化法可以得到: 1/1 ...

  8. python的内置函数bin()

    bin(x) 中文说明:将整数x转换为二进制字符串,如果x不为Python中int类型,x必须包含方法__index__()并且返回值为integer: 参数x:整数或者包含__index__()方法 ...

  9. Git Version recovery command introduction - git reset

    reset命令有3种方式: git reset –mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息 git re ...

  10. 隐藏nginx 版本号信息(转)

    为了安全,想将http请求响应头里的nginx版本号信息隐藏掉: 1. nginx配置文件里增加 server_tokens off; server_tokens作用域是http server loc ...