总体流程
详细步骤
获取你需要访问的Java对象的类
如果被Native调用的Java类是静态类:FindClass通过传java中完整的类名来查找java的class
如果是非静态类:GetObjectClass通过传入jni中的一个java的引用来获取该引用的类型
他们之间的区别是,前者要求你必须知道完整的类名,后者要求在Jni有一个类的引用
获取调用方法的MethodID
GetMethodID 得到一个实例的方法的ID
GetStaticMethodID 得到一个静态方法的ID
获取对象的属性
GetFieldID 得到一个实例的域的ID
GetStaticFieldID 得到一个静态的域的ID
JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数
jni里面调用java方法的环境分为2种:
第一种:在env所在线程调用java方法,这种情况不需要做特殊处理,直接按照步骤执行即可
第二种:在pthread子线程调用java方法,这种情况下就需要做处理了
在jni中,子线程中是不能直接调用JNIEnv对象的,也不能直接调用env线程中的jobject对象。因为jni中,JNIEnv是和线程相关的,每一个native方法所在线程就有一个当前线程相关的JNIEnv对象,而pthread线程中是不能调用native方法所在线程的JENnv对象的。
解决办法是:利用JavaVM虚拟机
JavaVM是和进程相关的,一个进程里面的JavaVM都是同一个,所以在pthread线程中就可以通过JavaVM来获取(AttachCurrentThread)当前线程的JNIEnv指针,然后就可以使用JNIEnv指针操作数据了。
还有在pthread线程中调用jobject对象时,首先需要把native线程里面的jobject创建全局引用(env->NewGlobalRef(jobj)),其返还的jobject对象就可以在程序中使用了,具体的调用函数的代码和函数的返回值相关,对应规则如下:
Instance Method Calling Routines:(静态方法CallStatic...
)
Call |
Native Type |
---|---|
CallVoidMethod | void |
CallObjectMethod | jobject |
CallBooleanMethod | jboolean |
CallByteMethod | jbyte |
CallCharMethod | jchar |
CallShortMethod | jshort |
CallIntMethod | jint |
CallLongMethod | jlong |
CallFloatMethod | jfloat |
CallDoubleMethod | jdouble |
在JNI_OnLoad中获取我们需要的JavaVM指针
//hello-jnicallback.h
void initClassHelper(JNIEnv *env, const char *path, jobject *objptr);
int handleCmd(const char * cmd);
static JavaVM *g_jvm = NULL;//java虚拟机
static jobject gInterfaceObject = NULL;//全局对象
static const char* const kClassPathName =
"com/tcl/hello/HelloAPI";
Java支持函数重载,利用方法签名和方法名来唯一确定一个JNI函数的调用
Java 类型 | 类型签名 |
---|---|
void | V |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
类 | L全限定名;,比如String, 其签名为Ljava/lang/String; |
数组 | [类型签名, 比如 [B |
参数String类型,返回值int类型:(Ljava/lang/String;)I
int func(int, String, int[]) => (ILjava/lang/String;[I)I
//hello-jnicallback.c
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
printf("-----------JNI_OnLoad-----------------");
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
printf("ERROR: JNI_OnLoad GetEnv failed\n");
goto bail;
}
assert(env != NULL);
g_jvm = vm;
/* success -- return valid version number */
result = JNI_VERSION_1_6;
initClassHelper(env, kClassPathName, &gInterfaceObject);
bail:
return result;
}
void initClassHelper(JNIEnv *env, const char *path, jobject *objptr)
{
jclass cls = (*env)->FindClass(env, path);
if(!cls) {
return;
}
jmethodID constr = (*env)->GetMethodID(env, cls, "" , "()V");
if(!constr) {
return;
}
jobject obj = (*env)->NewObject(env, cls, constr);
if(!obj) {
return;
}
(*objptr) = (*env)->NewGlobalRef(env, obj);
}
int handleCmd(const char * cmd)
{
int flag = -1;
JNIEnv *jniEnv = NULL;
jclass javaClass = NULL;
jmethodID javaMethod = NULL;
bool isAttached = false;
if(g_jvm == NULL)
return flag;
int status = (g_jvm)->GetEnv((void **) &jniEnv, JNI_VERSION_1_6);
if(status < 0)
{
status = g_jvm->AttachCurrentThread(&jniEnv, NULL);
if(status < 0)
return flag;
isAttached = true;
}
if(jniEnv == NULL )
{
g_jvm->DetachCurrentThread();
return flag;
}
javaClass = jniEnv->GetObjectClass(gInterfaceObject);
if(javaClass == NULL)
{
if(isAttached)
g_jvm->DetachCurrentThread();
return flag;
}
javaMethod = jniEnv->GetStaticMethodID(javaClass, "handleCmd","(Ljava/lang/String;)I");
if(javaMethod == NULL)
{
if(isAttached)
g_jvm->DetachCurrentThread();
return flag;
}
jstring jstrMSG = NULL;
jstrMSG =jniEnv->NewStringUTF(pkgName);
flag = jniEnv->CallStaticIntMethod(javaClass,javaMethod,jstrMSG);
jniEnv->DeleteLocalRef(jstrMSG);
if(isAttached)
g_jvm->DetachCurrentThread();
return flag;
}