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), pthread_self() = %ld", 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
#include
#include
#define LOG_TAG "JniHelper"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__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(), pthread_self() = %ld", thisthread);
return _psJavaVM;
}
void JniHelper::setJavaVM(JavaVM *javaVM) {
pthread_t thisthread = pthread_self();//是获得线程自身的ID
LOGD("JniHelper::setJavaVM(%p), pthread_self() = %ld", javaVM, thisthread);
_psJavaVM = javaVM;
pthread_key_create(&g_key, _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 *methodName,
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, 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 ((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", 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;
}
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。下面介绍一下参数
- JniMethodInfo &methodinfo
- const char *className
- const char *methodName
- const char *paramCode
这里重点说明一下这些参数的作用,
methodinfo:是一个结构体参数,JNIEnv*和java.lang.Class对象、函数ID。
这样就可以使用JNIEnv*调用 CallStaticXXXMethod(jclass clazz, jmethodID methodID, …)和 CallXXXMethod(jobject obj, jmethodID methodID, …)等常用函数(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