Jni 简单实战 写一个c调用java的辅助类

本节实现c调用java的静态方法

1、在jni文件夹下建

MyJniHelper.h,MyJniHelper.cpp 文件

2、编写MyJniHelper.h

#ifndef JNI_MYJNIHELPER_H_
#define JNI_MYJNIHELPER_H_
#include <jni.h>
#include <string.h>
namespace Hym {

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

class MyJniHelper {
private:
	//JavaVM虚拟机
	static JavaVM    *_psJavaVM;
	//ClassLoader类的loadClass方法的MethodID;
    static jmethodID _LoadClassMethodID;
    //ClassLoader 对象 为全局引用
    static jobject   _classLoader;
public:
	static void setJavaVM(JavaVM *javaVM);
	static JavaVM * getJavaVM();

	//从MainActivity实例获取 ClassLoader 与 loadClass
	static bool setClassLoaderFromInstance(jobject activityInstance);

	//获取java的静态方法
	static bool getStaticMethodInfo(JniMethodInfo &methodinfo,
	                                    const char *className,
	                                    const char *methodName,
	                                    const char *paramCode);
private:
	//获取当前线程的env
	static JNIEnv*	getEnv();
	//把线程中的env存在线程变量中
	static JNIEnv*  cacheEnv(JavaVM *jvm);
	//获取对应java类的jclass
	static jclass 	getClassID(const char *className);
	//设置 JniMethodInfo 信息
	static bool setMethodInfo(JniMethodInfo &methodInfo,
						const char *className,
						const char *methodName,
						const char *paramCode);

};


} /* namespace Hym */

#endif /* JNI_MYJNIHELPER_H_ */

3、编写 MyJniHelper.cpp文件

<span style="font-size:14px;">#include "MyJniHelper.h"
#include <pthread.h>
/*
 * 打开jni.h会发现
 * Prototypes for functions exported by loadable shared libs.  These are
 * called by JNI, not provided by JNI.
 * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
 * JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
 */
using namespace Hym;
/*
 * pthread_key_t
 * 为每一个线程中单独存储变量
 * 相当于每个线程都有pthread_key_t变量
 */
static pthread_key_t g_key;
JavaVM*   MyJniHelper::_psJavaVM         =nullptr;
jmethodID MyJniHelper::_LoadClassMethodID=nullptr;
jobject   MyJniHelper::_classLoader      =nullptr;

/*
 * JNIEnv 如何获取 JavaVM->GetEnv()
 * JavaVM 如何设置
 *
 * 首先获取类加载器
 * 如何获取类加载器
 * 获取类加载器有什么用
 */


/*
 * 一、设置JavaVM
 * 实现jni.h JNI_OnLoad方法
 * 设置线程变量
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
	Hym::MyJniHelper::setJavaVM(vm);
	return JNI_VERSION_1_4;
}

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

void MyJniHelper::setJavaVM(JavaVM *javaVM){
	_psJavaVM=javaVM;
	/*
	 * 调用 pthread_key_create() 来创建pthread_key_t变量。
	 * 该函数有两个参数,
	 * 第一个参数就是 pthread_key_t 变量,
	 * 第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。
	 * 该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
	 */
	pthread_key_create(&g_key,_detachCurrentThread);
}

JavaVM * MyJniHelper::getJavaVM(){
	return _psJavaVM;
}

/*
 * 二、获取 JNIEnv
 * 线程查找JNIEnv
 * 把JNIEnv变量存入线程变量
 */

//获取 JNIEnv
//如果线程中没有JNIEnv 则去创建并放到线程变量中
JNIEnv*	MyJniHelper::getEnv(){
	//获取线程变量 JNIEnv
	JNIEnv *env=(JNIEnv *)pthread_getspecific(g_key);
	if(env==nullptr){
		env=MyJniHelper::cacheEnv(_psJavaVM);
	}
	return env;
}

// 记录每个线程的 env
JNIEnv*  MyJniHelper::cacheEnv(JavaVM *jvm){
	JNIEnv *env=nullptr;
	//GetEnv()
	jint ret=jvm->GetEnv((void **)&env,JNI_VERSION_1_4);
	switch(ret){
		case JNI_OK:
			/* no error */
			//设置线程变量 JNIEnv
			pthread_setspecific(g_key,env);return env;
		case JNI_EDETACHED:
			/* thread detached from the VM */
			//确保它确实attach到Java环境里了。
			if(jvm->AttachCurrentThread(&env,nullptr)<0){
				return nullptr;
			}else{
				pthread_setspecific(g_key,env);
				return env;
			}
		case JNI_EVERSION:
			/* JNI version error */
		default :
			return nullptr;
	}
}


/*
 * 三、获取类加载器
 * 1、先获取 getClassLoader 的jmethodID
 * 2、通过 MainActivity对象 执行 getClassLoader方法 获取 ClassLoader
 * 3、获取 ClassLoader 的 loadClass方法 的 jmethodID
 * 4、之后就可以使用 ClassLoader.loadClass 获取 jclass
 */
//设置类加载器
bool MyJniHelper::setClassLoaderFromInstance(jobject activityInstance){

	JniMethodInfo _classLoaderMethod;
	if(!MyJniHelper::setMethodInfo(_classLoaderMethod,
					"android/content/Context",
					"getClassLoader",
					"()Ljava/lang/ClassLoader;")) {
		return false;
	}
	//activityInstance.getClassLoader()
	jobject _classLoader = MyJniHelper::getEnv()->CallObjectMethod(activityInstance,
			                            _classLoaderMethod.methodID);
	if(nullptr== _classLoader){
		return false;
	}
	//获取类加载器的loadClass方法
	JniMethodInfo _loadClassMethod;
	if (!MyJniHelper::setMethodInfo(_loadClassMethod,
					"java/lang/ClassLoader",
					"loadClass",
					"(Ljava/lang/String;)Ljava/lang/Class;")) {
		return false;
	}
	//全局引用
	MyJniHelper::_classLoader      =MyJniHelper::getEnv()->NewGlobalRef(_classLoader);
	MyJniHelper::_LoadClassMethodID=_loadClassMethod.methodID;
	return true;
}

//设置 JniMethodInfo
bool MyJniHelper::setMethodInfo(JniMethodInfo &methodInfo,
				const char *className,
				const char *methodName,
				const char *paramCode){
	if (nullptr == className
		||nullptr == methodName
		||nullptr == paramCode) {
		return false;
	}
	JNIEnv *env=MyJniHelper::getEnv();
	if(!env){
		return false;
	}
	/*
	 * FindClass 可以不使用加载器获取class
	 * 效果与之后使用一样 ClassLoader.loadClass 方法一样
	 */
	jclass classID = env->FindClass(className);
	if(!classID){
		env->ExceptionClear();
		return false;
	}
	/*
	 * GetMethodID          获取对象方法
	 * GetStaticMethodID    获取静态方法
	 */
	jmethodID methodID=env->GetMethodID(classID,methodName,paramCode);
	if(!methodID){
		env->ExceptionClear();
		return false;
	}

	methodInfo.classID=classID;
	methodInfo.methodID=methodID;
	methodInfo.env=env;
	return true;
}

/*
 * 四,获取 java的静态方法
 *1、先获取jclass
 *2、获取jmethodID
 *3、设置JniMethodInfo
*/

//获取 jclass
jclass MyJniHelper::getClassID(const char *className){
	if(nullptr==className){
		return nullptr;
	}
	JNIEnv* env	    =MyJniHelper::getEnv();
	jstring jClassName  =env->NewStringUTF(className);
	//使用ClassLoader.loadClass 获取class
	jclass jclazz       =(jclass)env->CallObjectMethod(MyJniHelper::_classLoader,
						MyJniHelper::_LoadClassMethodID,
						jClassName);
	if(nullptr== jclazz){
		env->ExceptionClear();
	}
	env->DeleteLocalRef(jClassName);
	return jclazz;
}

//获取静态方法
bool MyJniHelper::getStaticMethodInfo(JniMethodInfo &methodinfo,
				const char *className,
				const char *methodName,
				const char *paramCode){
	if(nullptr==className
	   || nullptr==methodName
	   || nullptr==paramCode){
		return false;
	}
	//获取 env
	JNIEnv *env=MyJniHelper::getEnv();
	if(env==nullptr){
		return false;
	}
	jclass classID=MyJniHelper::getClassID(className);
	if(! classID){
		env->ExceptionClear();
		return false;
	}
	jmethodID methodID=env->GetStaticMethodID(classID,methodName,paramCode);
	 if (! methodID) {
		env->ExceptionClear();
		return false;
	}
	methodinfo.classID  = classID;
	methodinfo.env      = env;
	methodinfo.methodID = methodID;
	return true;
}</span>








  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值