Skip to content

cocos2d x 3.x 026 移植cocos的jnihelper及用法说明

cheyiliu edited this page Dec 15, 2015 · 5 revisions

jni helper源码

  • 来自cocos 3.8, 头文件

#ifndef ANDROID_JNI_HELPER_H #define ANDROID_JNI_HELPER_H

#include <jni.h> #include

typedef struct JniMethodInfo_ { JNIEnv * env; jclass classID; jmethodID methodID; } JniMethodInfo;

class JniHelper { public: static void setJavaVM(JavaVM javaVM); static JavaVM getJavaVM(); static JNIEnv* getEnv();

static bool setClassLoaderFrom(jobject activityInstance);
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 jmethodID loadclassMethod_methodID;
static jobject classloader;

private: static JNIEnv* cacheEnv(JavaVM* jvm);

static bool getMethodInfo_DefaultClassLoader(JniMethodInfo &methodinfo,
		const char *className, const char *methodName,
		const char *paramCode);

static JavaVM* _psJavaVM;

};

#endif // ANDROID_JNI_HELPER_H


* .cpp

#include "JniHelper.h" #include "dlog.h" #include <android/log.h> #include <string.h> #include <pthread.h>

static pthread_key_t g_key;

jclass _getClassID(const char *className) { if (NULL == className) { return NULL; }

JNIEnv* env = JniHelper::getEnv();

jstring _jstrClassName = env->NewStringUTF(className);

jclass _clazz = (jclass) env->CallObjectMethod(JniHelper::classloader,
		JniHelper::loadclassMethod_methodID, _jstrClassName);

if (NULL == _clazz) {
	LOGE("Classloader failed to find class of %s", className);
	env->ExceptionClear();
}

env->DeleteLocalRef(_jstrClassName);

return _clazz;

}

void _detachCurrentThread(void* a) { JniHelper::getJavaVM()->DetachCurrentThread(); }

JavaVM* JniHelper::_psJavaVM = NULL; jmethodID JniHelper::loadclassMethod_methodID = NULL; jobject JniHelper::classloader = NULL;

JavaVM* JniHelper::getJavaVM() { pthread_t thisthread = pthread_self(); LOGD("JniHelper::getJavaVM(), pthread_self() = %ld", thisthread); return _psJavaVM; }

void JniHelper::setJavaVM(JavaVM *javaVM) { pthread_t thisthread = pthread_self(); LOGD("JniHelper::setJavaVM(%p), pthread_self() = %ld", javaVM, thisthread); _psJavaVM = javaVM;

pthread_key_create(&g_key, _detachCurrentThread);

}

JNIEnv* JniHelper::cacheEnv(JavaVM* jvm) { JNIEnv* _env = NULL; // get jni environment jint ret = jvm->GetEnv((void**) &_env, JNI_VERSION_1_4);

switch (ret) {
case JNI_OK:
	// Success!
	pthread_setspecific(g_key, _env);
	return _env;

case JNI_EDETACHED:
	// Thread not attached
	if (jvm->AttachCurrentThread(&_env, NULL) < 0) {
		LOGE("Failed to get the environment using AttachCurrentThread()");

		return NULL;
	} else {
		// Success : Attached and obtained JNIEnv!
		pthread_setspecific(g_key, _env);
		return _env;
	}

case JNI_EVERSION:
	// Cannot recover from this error
	LOGE("JNI interface version 1.4 not supported");
default:
	LOGE("Failed to get the environment using GetEnv()");
	return NULL;
}

}

JNIEnv* JniHelper::getEnv() { JNIEnv *_env = (JNIEnv *) pthread_getspecific(g_key); if (_env == NULL) _env = JniHelper::cacheEnv(_psJavaVM); return _env; }

bool JniHelper::setClassLoaderFrom(jobject activityinstance) { JniMethodInfo _getclassloaderMethod; if (!JniHelper::getMethodInfo_DefaultClassLoader(_getclassloaderMethod, "android/content/Context", "getClassLoader", "()Ljava/lang/ClassLoader;")) { return false; }

jobject _c = JniHelper::getEnv()->CallObjectMethod(activityinstance,
		_getclassloaderMethod.methodID);

if (NULL == _c) {
	return false;
}

JniMethodInfo _m;
if (!JniHelper::getMethodInfo_DefaultClassLoader(_m,
		"java/lang/ClassLoader", "loadClass",
		"(Ljava/lang/String;)Ljava/lang/Class;")) {
	return false;
}

JniHelper::classloader = JniHelper::getEnv()->NewGlobalRef(_c);
JniHelper::loadclassMethod_methodID = _m.methodID;

return true;

}

bool JniHelper::getStaticMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode) { if ((NULL == className) || (NULL == methodName) || (NULL == paramCode)) { return false; }

JNIEnv *env = JniHelper::getEnv();
if (!env) {
	LOGE("Failed to get JNIEnv");
	return false;
}

jclass classID = _getClassID(className);
if (!classID) {
	LOGE("Failed to find class %s", className);
	env->ExceptionClear();
	return false;
}

jmethodID methodID = env->GetStaticMethodID(classID, methodName, paramCode);
if (!methodID) {
	LOGE("Failed to find static method id of %s", methodName);
	env->ExceptionClear();
	return false;
}

methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;
return true;

}

bool JniHelper::getMethodInfo_DefaultClassLoader(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode) { if ((NULL == className) || (NULL == methodName) || (NULL == paramCode)) { return false; }

JNIEnv *env = JniHelper::getEnv();
if (!env) {
	return false;
}

jclass classID = env->FindClass(className);
if (!classID) {
	LOGE("Failed to find class %s", className);
	env->ExceptionClear();
	return false;
}

jmethodID methodID = env->GetMethodID(classID, methodName, paramCode);
if (!methodID) {
	LOGE("Failed to find method id of %s", methodName);
	env->ExceptionClear();
	return false;
}

methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;

return true;

}

bool JniHelper::getMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode) { if ((NULL == className) || (NULL == methodName) || (NULL == paramCode)) { return false; }

JNIEnv *env = JniHelper::getEnv();
if (!env) {
	return false;
}

jclass classID = _getClassID(className);
if (!classID) {
	LOGE("Failed to find class %s", className);
	env->ExceptionClear();
	return false;
}

jmethodID methodID = env->GetMethodID(classID, methodName, paramCode);
if (!methodID) {
	LOGE("Failed to find method id of %s", methodName);
	env->ExceptionClear();
	return false;
}

methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;

return true;

}


# 用法
1. 在jni onload里设置jvm

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { // other code // ...

JniHelper::setJavaVM(vm);
return JNI_VERSION_1_4;

}


2. 在java新增一native方法,参数为context。在loadLibrary之后,在合适的地方调用该方法

public native static int nativeInit(Context context);

在这个native的实现里,调用JniHelper::setClassLoaderFrom(obj); jint xxx(JNIEnv* env, jclass clazz, jobject obj) { JniHelper::setClassLoaderFrom(obj); return 0; }


3. 用法举例

int getDPIJNI() { JniMethodInfo t; jint ret = -1; if (JniHelper::getStaticMethodInfo(t, "org/cocos2dx/lib/Cocos2dxHelper", "getDPI", "()I")) { ret = t.env->CallStaticIntMethod(t.classID, t.methodID); t.env->DeleteLocalRef(t.classID); } return ret; }


# 其他
* 从调用方向看,JniHelper主要负责c call java, cocos的jni绝大部分采用c call 静态的java方法。原因很明显,简单!
* 另外一个原因是,调用对象方法或者对象属性,均可以通过静态方法来**拆分**实现,例如Cocos2dxHttpURLConnection.java这个类。
* java call c, 没什么好说的,也不是JniHelper的职责范围。
Clone this wiki locally