-
Notifications
You must be signed in to change notification settings - Fork 43
cocos2d x 3.x 026 移植cocos的jnihelper及用法说明
- 来自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的职责范围。
Just build something.