简介jni(二)

打开创建的本地头文件,可以看见要实现c++的方法:

/*
 * Class:     com_cn_TestJni
 * Method:    getjni
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_cn_TestJni_getjni
  (JNIEnv *, jobject);

/*
 * Class:     com_cn_TestJni
 * Method:    getj
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_cn_TestJni_getj
  (JNIEnv *, jclass);
下面来介绍下方法中JNIEnv ,jobject ,jclass代表的含义。

JNIEnv:

JNIEnv类型代表了Java环境。通过这个JNIEnv*z指针,可以对Java端的代码进行操作。例如创建Java类的对象,调用Java对象的方法,获取Java对象的属性等。JNIEnv的指针会被JNI传入到本地方法的实现函数中来对Java端的代码进行操作。

/*
 * JNI Native Method Interface.
 */

struct JNINativeInterface_;

struct JNIEnv_;

#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
jobject:

class _jobject {};
typedef _jobject *jobject;
本地方法是非静态方法,传递进来的jobject是指向java对象的引用,与调用该本地方法相关联的对象的引用。

jclass:

class _jobject {};
class _jclass : public _jobject {};
typedef _jclass *jclass;

本地方法是静态方法,传递进来的jclass代表了调用该静态方法类的class对象,即该Class类字节码。

jclass的取得:

为了能够在c/c++中使用Java类。JNI.h头文件中专门定义了jclass类型来表示Java中的Class类。

JNIEnv类中有如下几个简单的函数可以取得jclass:

 jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    jclass FindClass(const char *name) {
        return functions->FindClass(this, name);
    }
 jclass GetSuperclass(jclass sub) {
        return functions->GetSuperclass(this, sub);
    }
 jclass GetObjectClass(jobject obj) {
        return functions->GetObjectClass(this,obj);
    }
FindClass会在classpath系统环境变量下寻找类,传入完整类名,注意包与包之间用“/”分隔,如:

jclass cls_string=env->FindClass("java/lang/String");


java的数据类型跟c/c++的数据类型映射关系:

简介jni(二)_第1张图片

访问Java类中的属性与方法:

在本地代码中访问Java端的代码,一个常见的应用就是获取类的属性和调用类的方法,为了在c/c++中表示属性和方法,JNI在jni.h头文件中定义了jfieldID,jmethodID类型来分别代表Java端的属性和方法。

我们在访问或是设置Java属性的时候,首先就要在本地代码中取得代表该Java属性的jfieldID,然后才能在本地代码进行Java属性操作。同样的,我们需要呼叫Java端方法时,也是需要取得代表该方法的jmethodID才能进行java方法调用。

JNIEnv获取相应的jfieldID和jmethodID,可以看到它们的参数列表是相同的,是要获取java对象的属性/方法的该对象的Class ,属性或者方法名 ,属性或者方法类型:

 jfieldID GetFieldID(jclass clazz, const char *name,
                        const char *sig) {
        return functions->GetFieldID(this,clazz,name,sig);
    }
 jmethodID GetMethodID(jclass clazz, const char *name,
                          const char *sig) {
        return functions->GetMethodID(this,clazz,name,sig);
    }
 jfieldID GetStaticFieldID(jclass clazz, const char *name,
                              const char *sig) {
        return functions->GetStaticFieldID(this,clazz,name,sig);
    }
 jmethodID GetStaticMethodID(jclass clazz, const char *name,
                                const char *sig) {
        return functions->GetStaticMethodID(this,clazz,name,sig);
    }
GetMethodID也能够取得构造函数的jmethodID,创建一个java对象时可以调用指定的构造方法。
sign签名,用来表示要取得的属性或者方法的类型:

简介jni(二)_第2张图片

javap:

JDK提供的工具来查看一个类的声明。其中就可以设置输出每个属性/方法的签名。

简介jni(二)_第3张图片

效果:

public class TestJni {
	private String s;
	public TestJni() {
		super();
	};

	public String getS() {
		System.out.println("getS:" + s);
		return s;
	}

	public void setS(String s) {
		this.s = s;
		System.out.println("setS:" + s);
	}

	public native void getjni();
	public static native void getj();
}

简介jni(二)_第4张图片

获取/设置java属性:

取得属性相对应的jfieldID后,就可以使用set<Type>Field,get<Type>Field,setStatic<Type>Field和GetStatic<Type>Field等来对Java属性进行操作。

如取得/设置Int类型属性(其他类型类似):

jint GetIntField(jobject obj, jfieldID fieldID) {
        return functions->GetIntField(this,obj,fieldID);
    }
    void SetIntField(jobject obj, jfieldID fieldID,
                     jint val) {
        functions->SetIntField(this,obj,fieldID,val);
    }
  jint GetStaticIntField(jclass clazz, jfieldID fieldID) {
        return functions->GetStaticIntField(this,clazz,fieldID);
    }
void SetStaticIntField(jclass clazz, jfieldID fieldID,
                        jint value) {
      functions->SetStaticIntField(this,clazz,fieldID,value);
    }
java方法的调用:

JNIEnv提供了Call<Type>Method ,CallStatic<Type>Method和CallNonVirtual<Type>Method,通过传入的jmethodID来来调用方法。

调用实例方法的三种形式:

 jint CallIntMethod(jobject obj, jmethodID methodID, ...) {
        va_list args;
        jint result;
        va_start(args,methodID);
        result = functions->CallIntMethodV(this,obj,methodID,args);
        va_end(args);
        return result;
    }
    jint CallIntMethodV(jobject obj, jmethodID methodID,
                        va_list args) {
        return functions->CallIntMethodV(this,obj,methodID,args);
    }
    jint CallIntMethodA(jobject obj, jmethodID methodID,
                        const jvalue * args) {
        return functions->CallIntMethodA(this,obj,methodID,args);
    }
第一种是最常用的方法。

第二种是当调用这个函数的时候有一个指向参数表的va_list变量时使用

第三种是当调用这个函数的时候有一个指向jvalue或jvalue数组的指针时用

调用静态方法的三种形式:

    jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID,
                             ...) {
        va_list args;
        jobject result;
        va_start(args,methodID);
        result = functions->CallStaticObjectMethodV(this,clazz,methodID,args);
        va_end(args);
        return result;
    }
    jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID,
                              va_list args) {
        return functions->CallStaticObjectMethodV(this,clazz,methodID,args);
    }
    jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID,
                              const jvalue *args) {
        return functions->CallStaticObjectMethodA(this,clazz,methodID,args);
    }
实例:

在java端声明属性和方法:

public class TestMain {
	public static int i = 10;
	public void tPrint(int i) {
		System.out.println("call tPrint:" + i);
	}
	public native void getjni();

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.loadLibrary("ConsoleApplication");
		TestMain j = new TestMain();
		j.getjni();
		System.out.println(j.i);
	}
}
在c++中调用属性和方法;

JNIEXPORT void JNICALL Java_com_cn_TestMain_getjni
	(JNIEnv * env, jobject obj){
		//获取class
		jclass cls=env->GetObjectClass(obj);
		
		/** 属性*/
		//获取jfieldID
		jfieldID iid=env->GetStaticFieldID(cls,"i","I");
		//获取属性
		jint iint=env->GetStaticIntField(cls,iid);
		cout<<iint<<endl;
		//设置属性
		env->SetStaticIntField(cls,iid,20L+iint);
		/** 方法*/
		//获取jmethodID
		jmethodID mid=env->GetMethodID(cls,"tPrint","(I)V");
		//调用方法
		env->CallVoidMethod(obj,mid,15L);
};
打印结果;



callNonvirtual<Type>Method:
在JNI中顶以的CallNonVirtual<Type>Method能够实现子类调用父类方法的功能。如果想要调用一个对象的父类的方法,而不是子类的这个方法的话,就可以使用。

使用前,先要取得父类及其要调用的父类方法的jmethodID,传入到这个方法中就能通过子类对象调用被覆盖的父类方法了。

实例:

java端定义父类和子类;

public class TestFather {
	public void function() {
		System.out.println("call father: function");
	}
}

public class TestChild extends TestFather {

	public void function() {
		// TODO Auto-generated method stub
		System.out.println("call child: function");
	}
}
在入口处声明:

public class TestMain {
	public native void getjni();
	TestFather f = new TestChild();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.loadLibrary("ConsoleApplication");
		TestMain j = new TestMain();
		j.getjni();
	}
}
在c++中:

JNIEXPORT void JNICALL Java_com_cn_TestMain_getjni
	(JNIEnv * env, jobject obj){
		jclass cls=env->GetObjectClass(obj);
		
		jfieldID fid=env->GetFieldID(cls,"f","Lcom/cn/TestFather;");
		jobject fobj=env->GetObjectField(obj,fid);

		jclass cls_f=env->FindClass("com/cn/TestFather");
		jmethodID methodid_f=env->GetMethodID(cls_f,"function","()V");
		env->CallVoidMethod(fobj,methodid_f);
		env->CallNonvirtualVoidMethod(fobj,cls_f,methodid_f);
};
结果:


注意:

1.javac 找不到符号

简介jni(二)_第5张图片

把关联的类都编译下:javac *.java  就可以了。

你可能感兴趣的:(jni)