微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JniHelper详细说明

1.Jni的介绍

先简单介绍一下,Jni简称(Java Native Interface)Java原生接口,在Java里面用Native修饰的方法由另外一种语言实现的本地方法,意思就是说,这个用native修饰的方法在Java里面只能看到声明看不到定义。
再简单来说,这个Jni的作用就是Java和其他语言的交互,其他语言可以调用Java相关环境,Java也可以用Jni技术来调用其他语言来弥补自身的不足,例如访问系统底层,嘿嘿。
下面这张图介绍了JNI的基本结构描述图:


2.在C++中调用Java方法

c++调用java需要获取java的运行环境和JVM。
JVM:是java虚拟机,所有的交互工作都是从获取jvm开始的,获取JVM的时候需要注意下面,就是要告知系统使用哪一版本的JNI不规定的话认使用1.1版本。最好是使用新版本JNI_VERSION_1_4,就是1.4版本。
    JavaVM* JniHelper::_psJavaVM = nullptr;
    jmethodID JniHelper::loadclassMethod_methodID = nullptr;
    jobject JniHelper::classloader = nullptr;

    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),javaVM,thisthread);
        _psJavaVM = javaVM;

        pthread_key_create(&g_key,_detachCurrentThread);
    }
这上面的代码是Cocos里面封装好的JniHelper里面的获取JVM的一些相关操作,
void _detachCurrentThread(void* a) {
    cocos2d::JniHelper::getJavaVM()->DetachCurrentThread();
}

这个方法要Detach以下当前线程。
然后及时获取环境了,我把里面的代码都贴出来
#include "JniHelper.h"
#include <android/log.h>
#include <string.h>
#include <pthread.h>

#define  LOG_TAG    "JniHelper"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,__VA_ARGS__)

static pthread_key_t g_key;//用于声明线程ID

jclass _getClassID(const char *className) {//获取类ID
    if (nullptr == className) {
        return nullptr;
    }

    jnienv* env = cocos2d::JniHelper::getEnv();//环境变量

    jstring _jstrClassName = env->NewStringUTF(className);//需要调用java的类名

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

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

    env->DeleteLocalRef(_jstrClassName);
        
    return _clazz;
}

void _detachCurrentThread(void* a) {
    cocos2d::JniHelper::getJavaVM()->DetachCurrentThread();//撤销当前线程
}

namespace cocos2d {

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

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

    void JniHelper::setJavaVM(JavaVM *javaVM) {
        pthread_t thisthread = pthread_self();//是获得线程自身的ID
        LOGD("JniHelper::setJavaVM(%p),_detachCurrentThread);
    }

    jnienv* JniHelper::cacheEnv(JavaVM* jvm) {
        jnienv* _env = nullptr;
        // get jni environment
        jint ret = jvm->GetEnv((void**)&_env,JNI_VERSION_1_4);//获取1.4的Jni环境
        
        switch (ret) {
        case JNI_OK :
            // Success!
            pthread_setspecific(g_key,_env);
            return _env;
                
        case JNI_EDETACHED :
            // Thread not attached
            if (jvm->AttachCurrentThread(&_env,nullptr) < 0)
                {
                    LOGE("Failed to get the environment using AttachCurrentThread()");

                    return nullptr;
                } 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 nullptr;
        }
    }

    jnienv* JniHelper::getEnv() {
        jnienv *_env = (jnienv *)pthread_getspecific(g_key);
        if (_env == nullptr)
            _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 = cocos2d::JniHelper::getEnv()->CallObjectMethod(activityinstance,_getclassloaderMethod.methodID);

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

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

        JniHelper::classloader = cocos2d::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 ((nullptr == className) ||
            (nullptr == methodName) ||
            (nullptr == 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 *paramCode) {
        if ((nullptr == className) ||
            (nullptr == methodName) ||
            (nullptr == 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,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 *paramCode) {
        if ((nullptr == className) ||
            (nullptr == methodName) ||
            (nullptr == paramCode)) {
            return false;
        }

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

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

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

        return true;
    }

    std::string JniHelper::jstring2string(jstring jstr) {
        if (jstr == nullptr) {
            return "";
        }
        
        jnienv *env = JniHelper::getEnv();
        if (!env) {
            return nullptr;
        }

        const char* chars = env->GetStringUTFChars(jstr,nullptr);
        std::string ret(chars);
        env->ReleaseStringUTFChars(jstr,chars);

        return ret;
    }

} //namespace cocos2d

这里面实现了在C++方面获取到Java的虚拟机,还有java运行环境,还有需要调用的类名等,上面的getStaticmethodInfo方法用来判断Java的类静态函数是否存在,并且初始化info。下面介绍一下参数
  1. JniMethodInfo &methodinfo
  2. const char *className
  3. const char *methodName
  4. const char *paramCode
这里重点说明一下这些参数的作用,
methodinfo:是一个结构体参数,jnienv*和java.lang.class对象、函数ID。 这样就可以使用jnienv*调用 CallStaticXXXMethod(jclass clazz,jmethodID methodID,…)和 CallXXXMethod(jobject obj,…)等常用函数(XXX替换为函数返回值类型,如:Void,Int等)。
className:这是你需要调用函数所在的类。
methodName:这个是方法名,就是你想调用方法
paramCode:这是参数签名,因为java是支持多态的,如果不加这个参数,java无法判断到具体函数
有关签名类型可以参考一下说明:


如果一切正常这个getStaticmethodInfo会返回真,并且初始化info结构体,现在和java交互的工作就已经完成的差不多了,下面就是我们需要用info这个结构体要做的一些事情了:
这里要贴这个结构体的有关信息
typedef struct JniMethodInfo_
{
    jnienv *    env;
    jclass      classID;
    jmethodID   methodID;
} JniMethodInfo;


Oracle上面有关jni的文档,走你

下面我把info的代码贴出来。
bool isHave = JniHelper::getStaticmethodInfo(minfo,JAVA_CLASSNAME,"LoginWX","(Ljava/lang/String;Ljava/lang/String;)V");
		if (isHave)
		{
			jstring jAPP_ID = minfo.env->NewStringUTF(APP_ID);										 
			jstring jAppSecret = minfo.env->NewStringUTF(AppSecret);
			minfo.env->CallStaticVoidMethod(minfo.classID,minfo.methodID,jAPP_ID,jAppSecret);

			minfo.env->DeleteLocalRef(jAPP_ID);
			minfo.env->DeleteLocalRef(jAppSecret);
			minfo.env->DeleteLocalRef(minfo.classID); 
			cocos2d::log("JniFun call LoginWX over!");
		}
		else
		{
			//NoticeMsg::Instance().Showlogon(false);
			cocos2d::log("JniFun call LoginWX error!");
		}


上面再C++这方面就调用了Java里面的有关微信登录函数
这里就先这样吧,简简单单说了一下Jni的作用,后面更详细的内容深入了解之后再做记录,就这样。
QQ:763949771,有问题加我QQ深入交流一下,备注:csdn

原文地址:https://www.jb51.cc/cocos2dx/338957.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐