本文已参与「新人创作礼」活动,一起开启掘金创作之路。
简单介绍一些JNI的基础使用,介绍如何使用JNI进行简单的java和C/C++之间的相互调用,同时也作为自己的学习笔记
1,定义一个java的静态方法
在Java层面上编写一个Java方法。
//我们就在MainActivity中定义一个静态的方法
public static void JNIcallJava(){
Log.i("test class","when you have seen this you are sucess");
}
2,JNI使用该方法
extern "C"
JNIEXPORT void JNICALL
Java_com_example_administrator_myjnitest_MainActivity_callMethod(JNIEnv *env, jobject instance) {
LOGI("now begin to call the java static method");
// 这里是查找该方法在java的那个类中,注意这里的包名路径要写正确,精确到类名
jclass class_method = env->FindClass("com/example/administrator/myjnitest/MainActivity");
//我们根据类名来查找具体的方法,注意下面的第三个参数表示无参数无返回值void
jmethodID md = env->GetStaticMethodID(class_method,"JNIcallJava","()V");
//调用这个java的静态方法
env->CallStaticVoidMethod(class_method,md);
}
java有两类域:实例域和静态域
JNI提供了访问两类域的函数
带有静态域和实例域的java类
public class JavaClass{
//实例域
private String instanceFiled = "Instance Field";
//静态域
private static String staticField = "Static Field";
}
一获取域ID
JNI提供了用域ID访问两类域的方法,可以通过给定的实例class对象来获取域ID,用GetObjectClass函数来获取class对象
//用对象引用获取类
jclass clazz;
clazz = (*env)->GetObjectClass(env, instance);
//获取实例域的域ID
jfieldID instanceField;
instanceField = (*env)->GetField(env,clazz, "instanceField", "Ljava/lang/String");
//获取静态域的ID
jfieldID staticFieldId;
staticFieldId = (*env)->GetStaticField(env,clazz, "staticField", "Ljava/lang/String");
二,获取域
在获取域ID之后,可以用GetField 函数获得实际的实例域
//获取实例域
jstring instanceField;
instanceField = (*env)->GetObjectField(env, instance, instanceFieldID);
//获取静态域
jstring staticField;
staticField = (*env)->GetStaticObjectField(env,clazz,staticFieldId);
//在内存溢出的情况下,这些函数均返回NULL,原生代码不会继续执行
调用方法
java类
public class JavaClass{
//实例方法
private String instanceMethod(){
return "instance Methid";
}
//静态方法
private static String staticMethod(){
return "Static Method";
}
}
一 获取方法ID
//获取实例方法的ID
jmethod instanceMethodID;
instanceMethodId = (*env)->GetMethod(env, clazz, "instanceMehtod", "()Ljava/lang/String;");
//获取静态方法的ID
jmethodID staticMethodId;
staticMethodId = (*env)->GetStaticMethod(env, clazz, "staticMethod", "()Ljava/lang/String;");
二 根据ID调用方法
//调用实例方法
jstring instanceMethodResult;
instanceMethodResult = (*env)->CallStringMethod(env,instacne, instanceMethod);
//调用静态方法
jstring staticMethodResult;
staticMethodResult = (*env)->CallStaticStringMethod(env, clazz, staticMethodId)
##我最近看了很多JNI的网页,基本上都是JNI环境配置,然后就没有了,Android studio到了3.0这个版本环境都是死的,初始化都有一个最基本的native-lib.cpp这个文件,运行就可以在界面上显示从C++返回的字符串,今天这里就不介绍这个了,太多了。一点意义都没有。
1 如何添加C语言单独文件
###新建C语言文件(当然你也可以新建C++文件):
这里新建完成后我们就可以到这里面写代码了 ,还是一样可以在activity中根据方法名来调用
2, cmake和 C/C++之间的构建 Cmake文件介绍(很多初级的JNI程序员都不知道怎么去看cmake文件,网上都是教人怎么配置JNI环境)
异常处理是java程序设计语言的重要功能,JNI的异常行为与java的有所不同,在java中当抛出一个异常的时候,虚拟机停止执行代码并进入调用栈反向检查特定类型的异常。虚拟机清除异常并将控制权交给异常处理程序。相比之下JNI要求开发人员在异常发生后显示的实现异常处理流。
捕捉异常
抛出异常的java例子
public class JavaClass{
//抛出方法
private void throwingMethod() throws NullPointerException{
throw new NullPointerException("NUll pointer");
}
//访问原生方法
private native void accessMethods();
}
原生代码中的异常处理
jthrowable ex;
(*env)->CallVoidMethod(env, instance, throwingMethod);
ex = (*env)->ExceptionOccurred(env);
if(0!=ex){
(*env)->ExceptionClear(env);
//在这里处理异常
}
/*
*在调用throwingMethod方法时,accessMethod原生方法需要显示的做异常处理JNI提供了ExceptionOccurred函数查询虚拟机中是否有挂起的。在使用完之后,异常处理程序需要用ExceptionClear函数显示的清除异常。
*
*/
抛出异常
JNI也允许远程代码抛出异常.因为异常时java类。应该先用FindClass函数找到异常类。用ThrowNew函数可以初始化并且抛出新的异常
/*
因为原生代码执行不受虚拟机得控制,因此抛出异常并不会停止原生函数的执行并把控制权交给异常处理程序。到异常抛出的时候,原生函数应该释放所有已分配的原生资源,例如内存及合适的返回值等,通过JNIEnv接口获得的引用是局部引用且一旦返回原生函数,他们自动的被虚拟机释放。
*/
jclass clazz;
clazz = (*env)->FindClass(env, "java/lang/NullPointerException");
if(0!=claszz){
(*env)->ThrowNew(env, clazz, "Exception message");
}