最近的一个cocos项目有使用到JNI,结果又是通过百度来查找使用方法,为了方便以后查看,在这里小结一下,应该会比较适合初次使用Jni的小伙伴们:)
JNI是Java Native Interface的缩写,中文为Java本地调,是Java语言提供的一种通用接口,用于Java代码与本地化代码的交互。
所谓本地化代码(Native Code)是指已被编译为特定于处理器的二进制代码,如Windows下的DLL,MAC OS X下的SO文件。
通过JNI可以实现C++与Java的相互调用。
这里主要是讲解在Cocos2d-x中通过Jni调用Java方法。
以下将会从创建一个新项目开始,完整讲解Jni的使用过程
1、创建一个Cocos2d-x的C++项目,包名“com.game.UseJni”
2、在Eclipse中导入项目中的Android工程,在src目录下新建包“com.game.JniMethod”,并在包中新建一个java件“JniMethod.java”。这里的名字都是随意取的,在C++调用时会用到。
在JniMethod.java中写了一些静态方法,供C++中调用
JniMethod.java
- package com.game.JniMethod;
- import android.util.Log;
- public class JniMethod
- {
-
- public static void AndroidFunc1()
- {
- Log.d("jni", "AndroidFunc1 called");
- }
-
- public static void AndroidFunc2(float number)
- {
- Log.d("jni", "AndroidFunc2 called: " + number);
- }
-
- public static void AndroidFunc3(String name)
- {
- Log.d("jni", "AndroidFunc3 called: " + name);
- }
-
- public static void AndroidFunc4(String name, int age)
- {
- Log.d("jni", "AndroidFunc4 called: " + age);
- }
-
- public static int AndroidFunc5(boolean flag)
- {
- Log.d("jni", "AndroidFunc5 called");
- return flag ? 10 : 100;
- }
-
- public static void AndroidFunc6(int arr[])
- {
- int sum = 0;
- for (int i : arr)
- {
- sum += i;
- }
- Log.d("jni", "AndroidFunc6 called: " + sum);
- }
-
- public static int[] AndroidFunc7()
- {
- int arr[] = {10, 20, 30, 40};
- return arr;
- }
- }
3、用xcode 打开项目的C++工程, 在Class目录下新建C++类JniMethod.h、JniMethod.cpp,在这个类中调用刚才写的Java静态方法。
JniMethod.h
-
-
-
-
-
-
-
-
- #ifndef __UseJni__JniMethod__
- #define __UseJni__JniMethod__
-
- #include <stdio.h>
-
- #if defined(ANDROID)
-
- class JniMethod
- {
- public:
- static void callAndroidFunc1();
- static void callAndroidFunc2(float number);
- static void callAndroidFunc3(std::string name);
- static void callAndroidFunc4(std::string name, int age);
- static int callAndroidFunc5(bool flag);
- static void callAndroidFunc6(std::vector<int> arr);
- static std::vector<int> callAndroidFunc7();
- };
-
- #endif
- #endif /* defined(__UseJni__JniMethod__) */
JniMethod.cpp
-
-
-
-
-
-
-
-
- #include "cocos2d.h"
- #include "JniMethod.h"
-
- #if defined(ANDROID)
- #include "platform/android/jni/JniHelper.h"
-
- USING_NS_CC;
-
-
-
- void JniMethod::callAndroidFunc1()
- {
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc1", "()V");
-
- CCASSERT(isHave, "jni callAndroidFunc1 not found");
-
- if (isHave)
- {
- minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID);
- }
-
- }
-
-
- void JniMethod::callAndroidFunc2(float number)
- {
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc2", "(F)V");
-
- CCASSERT(isHave, "jni callAndroidFunc1 not found");
-
- if (isHave)
- {
- minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, number);
- }
- }
-
-
- void JniMethod::callAndroidFunc3(std::string name)
- {
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc3", "(Ljava/lang/String;)V");
-
- CCASSERT(isHave, "jni callAndroidFunc3 not found");
-
- if (isHave)
- {
- jstring jStr = minfo.env->NewStringUTF(name.c_str());
- minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, jStr);
- }
- }
-
-
- void JniMethod::callAndroidFunc4(std::string name, int age)
- {
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc4", "(Ljava/lang/String;I)V");
-
- CCASSERT(isHave, "jni callAndroidFunc4 not found");
-
- if (isHave)
- {
- jstring jName = minfo.env->NewStringUTF(name.c_str());
- minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, jName, age);
- }
- }
-
-
- int JniMethod::callAndroidFunc5(bool flag)
- {
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc5", "(Z)I");
-
- CCASSERT(isHave, "jni callAndroidFunc5 not found");
-
- if (isHave)
- {
- jint num = minfo.env->CallStaticIntMethod(minfo.classID, minfo.methodID, flag);
- return num;
- }
- return -1;
- }
-
-
- void JniMethod::callAndroidFunc6(std::vector<int> arr)
- {
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc6", "([I)V");
-
- CCASSERT(isHave, "jni callAndroidFunc6 not found");
-
- if (isHave)
- {
- jintArray jarr = minfo.env->NewIntArray(arr.size());
- jint *intArr = minfo.env->GetIntArrayElements(jarr, NULL);
- for (int i = 0; i < arr.size(); ++i)
- {
-
- intArr[i] = arr[i];
- }
- minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, jarr);
- }
- }
-
-
- std::vector<int> JniMethod::callAndroidFunc7()
- {
- std::vector<int> arr;
- JniMethodInfo minfo;
- bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/game/JniMethod/JniMethod", "AndroidFunc7", "()[I");
-
- CCASSERT(isHave, "jni callAndroidFunc7 not found");
-
- if (isHave)
- {
- jintArray jarr = (jintArray)minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID);
- jsize len = minfo.env->GetArrayLength(jarr);
- jint *intArr = minfo.env->GetIntArrayElements(jarr, NULL);
- for (int i = 0; i < len; ++i)
- {
- arr.push_back(intArr[i]);
- }
- }
-
- return arr;
- }
-
- #endif
4、在HelloWorldScene.cpp中调用JniMethod.cpp的方法,这里是直接放到bool HelloWorld::init()方法中调用的
- #if defined(ANDROID)
-
- JniMethod::callAndroidFunc1();
- JniMethod::callAndroidFunc2(3.14f);
- JniMethod::callAndroidFunc3("hello");
- JniMethod::callAndroidFunc4("hello", 3);
-
- int ret = JniMethod::callAndroidFunc5(true);
- log("callAndroidFunc5: %d", ret);
-
- std::vector<int> arr6;
- arr6.push_back(1);
- arr6.push_back(20);
- arr6.push_back(300);
- JniMethod::callAndroidFunc6(arr6);
-
- std::vector<int> arr7 = JniMethod::callAndroidFunc7();
- for (int i : arr7)
- {
- log("callAndroidFunc7: %i", i);
- }
-
- #endif
5、修改UseJni/proj.android/jni工程目录下的Android.mk文件,添加JniMethod.cpp
6、在终端运行UseJni/proj.android目录下的build_native.py,编译成功,可以看到生成了SO文件
7、回到Eclipse,刷新安卓工程然后运行,可以在日志中查看运行结果
Tag为“jni”的是在安卓中自己输出的日志,Tag为“Cocos2d-x debug info”的是在C++中输出的日志
8、程序写完了,这里说明一下上面的函数使用方法,主要是JniMethod.cpp中的内容
-
-
-
-
-
- bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/game/JniMethod/JniMethod","AndroidFunc1","()V");
参数返回值类型 的签名(Signature)与Java类型 的对应关系,有了这个对应关系以上写的函数就很好理解了
-
- minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID)
-
-
-
下面列出常用的一些调用方法
callAndroidFunc7 中 jint *intArr = minfo.env->GetIntArrayElements(jarr,NULL);
通过函数GetIntArrayElements来获得jintArray中的数组指针,然后通过指针操作数组元素
这里给出其它数组类型的函数,通过看Array Type的类型,可以看到不同类型的数组有不同的JNI类型名
9、参考文献
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html#wp20949
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html#wp23720
http://www.linuxidc.com/Linux/2011-10/44997.htm
http://xiaominghimi.blog.51cto.com/2614927/908818/
http://public0821.iteye.com/blog/423941
http://blog.csdn.net/jiangwei0910410003/article/details/17653803