最近的一个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的使用过程
在JniMethod.java中写了一些静态方法,供C++中调用
package com.game.JniMethod;
import android.util.Log;
public class JniMethod
{
// 不带参数
public static void AndroidFunc1()
{
Log.d("jni", "AndroidFunc1 called");
}
// 带一个float参数
public static void AndroidFunc2(float number)
{
Log.d("jni", "AndroidFunc2 called: " + number);
}
// 带一个String参数
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);
}
// 带一个boolean参数,并返回一个int
public static int AndroidFunc5(boolean flag)
{
Log.d("jni", "AndroidFunc5 called");
return flag ? 10 : 100;
}
// 带一个int 数组参数
public static void AndroidFunc6(int arr[])
{
int sum = 0;
for (int i : arr)
{
sum += i;
}
Log.d("jni", "AndroidFunc6 called: " + sum);
}
// 返回一个int 数组
public static int[] AndroidFunc7()
{
int arr[] = {10, 20, 30, 40};
return arr;
}
}
//
// JniMethod.h
// UseJni
//
// Created by try on 14-10-3.
//
//
#ifndef __UseJni__JniMethod__
#define __UseJni__JniMethod__
#include
#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 arr);
static std::vector callAndroidFunc7();
};
#endif
#endif /* defined(__UseJni__JniMethod__) */
JniMethod.cpp
//
// JniMethod.cpp
// UseJni
//
// Created by try on 14-10-3.
//
//
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "cocos2d.h"
#include "JniMethod.h"
#if defined(ANDROID)
#include "platform/android/jni/JniHelper.h"
USING_NS_CC;
// 调用android静态方法:没有参数
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);
}
}
// 调用android静态方法:一个float参数
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);
}
}
// 调用android静态方法:一个string参数
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);
}
}
// 调用android静态方法:多个参数
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 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)
{
// http://www.2cto.com/kf/201312/268735.html
intArr[i] = arr[i];
}
minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, jarr);
}
}
//
std::vector JniMethod::callAndroidFunc7()
{
std::vector 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
#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 arr6;
arr6.push_back(1);
arr6.push_back(20);
arr6.push_back(300);
JniMethod::callAndroidFunc6(arr6);
std::vector arr7 = JniMethod::callAndroidFunc7();
for (int i : arr7)
{
log("callAndroidFunc7: %i", i);
}
#endif
// 定义Jni函数信息结构体
JniMethodInfo minfo;
// 查找函数是否存在
// 第二个参数为函数所在的包名+类名,注意以“/”分隔
// 第三个参数为函数名
// 最后一个参数是函数的参数与返回值的描述信息,括号里的是传入的参数类型,括号右边的是返回值类型,
// AndroidFunc1为无参所以括号内为空没有返回值,括号右边的返回值类型为V
bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/game/JniMethod/JniMethod","AndroidFunc1","()V");
// AndroidFunc2的参数描述信息是"(F)V",传入参数为float(签名F),返回值为空(签名V)
// AndroidFunc4的参数描述信息是"(Ljava/lang/String;I)V",传入参数为String(签名Ljava/lang/String;)和int(符号I),返回值为空(签名V)
// AndroidFunc6的参数描述信息是"([I)V",传入参数为int[](签名[I),返回值为int(签名I)
参数返回值类型 的签名(Signature)与Java类型 的对应关系,有了这个对应关系以上写的函数就很好理解了
// 调用Java静态函数,前两个参数为固定写法,从第三个参数起可以加自己传入的参数
minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID)
// 没有返回值的静态函数调用为 CallStaticVoidMethod,注意函数名中的Void
// 返回int的静态函数调用为CallStaticIntMethod,注意函数名中的 Int,我在这犯了不少错。。。
下面列出常用的一些调用方法
callAndroidFunc7 中 jint *intArr = minfo.env->GetIntArrayElements(jarr,NULL);
通过函数GetIntArrayElements来获得jintArray中的数组指针,然后通过指针操作数组元素
这里给出其它数组类型的函数,通过看Array Type的类型,可以看到不同类型的数组有不同的JNI类型名
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