在上一篇中,讲了如何CMake生成so动态库,这里写一下简单的语法操作。
java调用native方法返回值。
第一步:在java文件中写native方法。
public native String test(String name,int age);
第二步:在对应的cpp文件中创建native方法。语法如下:
方法名的组成是:Java_后面接包名,点用下划线代替,再接方法名。
第一行有一个是代表返回值类型。
extern "C" JNIEXPORT string JNICALL
Java_com_example_jnitwo_MainActivity_test(JNIEnv *env, jobject thiz,jstring name,jint age) {//第一个参数:JNI环境
string str = env->GetStringUTFChars(name,0);
return str;
}
(上面代码中使用了using namespace std;
所以可以省却std::)
如果返回的是基本数据类型,可以直接返回
第三步:引入so文件。
static {
System.loadLibrary("one-test");
}
第四步:运行native方法,接收返回值
String name = test("name", 12);
如果传过来的是数组类型:
jintArray age
//第二个参数:指针指向以一个地址
jint *p = env->GetIntArrayElements(age, NULL);
//获得数组长度
int32_t s = env->GetArrayLength(age);
/*参数三:
0:刷新java数组并释放c/c++数组。
1=JNI_COMMIT:只刷新java数组
2=JNI_ABORT:只释放:
*/
env->ReleaseIntArrayElements(age,p,0);
当native方法传入类对象和c调用java代码
public native void test(Bean bean);
Bean类:
public class Bean {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void printInfo(String msg){
Log.e("TAG", msg);
}
}
cpp类:(通过反射来获得类的方法)
Java_com_example_jnitwo_MainActivity_test(JNIEnv *env, jobject thiz, jobject bean) {
//反射调用java方法
//1,获取java对应的class对象
jclass cls = env->GetObjectClass(bean);
//2,找到要调用的方法
//参数3:签名
//get方法
jmethodID get = env->GetMethodID(cls, "getAge", "()I");
//3,调用
jint i = env->CallIntMethod(bean, get);
//set方法
jmethodID set = env->GetMethodID(cls, "setAge", "(I)V");
env->CallVoidMethod(bean,set,22);
//static方法
jmethodID printInfo = env->GetStaticMethodID(cls, "printInfo", "(Ljava/lang/String;)V");
//创建java字符串
jstring js = env->NewStringUTF("在吗");
env->CallStaticVoidMethod(cls,printInfo,js);
//释放局部引用
env->DeleteLocalRef(js);
}
注释:这里的前面括号内的是bean类对应方法的参数。括号外面的是返回值。如果是类的话,则需要写“L+java…/(对应包下的对应类)"。
获得对应的属性,并修改
jclass cls = env->GetObjectClass(bean);
//获得值
jfieldID id = env->GetFieldID(cls, "age", "I");
//修改
env->SetIntField(bean,id,30);
如果想在cpp调用这样的构造方法:
public static void getBean(Bean2 bean) {
Log.e("TAG", bean.i+"");
}
则操作如下:
1,先获得bean类的该方法。
2,获得bean2类对象。
3,给bean2类对象初始化,调用其构造方法
4,在bean类运行该方法。
jmethodID getbean = env->GetStaticMethodID(cls, "getBean", "(Lcom/example/jnitwo/Bean2;)V");
//在jni中创建java类对象
jclass abc = env->FindClass("com/example/jnitwo/Bean2");
//获得类的构造方法
jmethodID init = env->GetMethodID(abc,"<init>", "(I)V");
//调用构造方法
jobject obj = env->NewObject(abc, init,88);
env->CallStaticVoidMethod(cls,getbean,obj);
直接调用普通方法:
Java_com_example_jnionload_MainActivity_stringFromJNI(JNIEnv* env,jobject obj) {
//获得类对象
jclass clazz = env->FindClass("com/example/jnionload/MainActivity");
//获得类方法
jmethodID init = env->GetMethodID(clazz,"logTest", "()V");
//调用方法
env->CallVoidMethod(obj,init);
}
上面总结:
当你调用类方法不是静态的话。则你java需要传递一个类作为参数传过来。
如果是静态方法,则可以通过FindClass的对象去执行该方法。例如:
//不用传递类对象过来,执行方法。
jclass cls = env->FindClass("com/example/jnitwo/Bean");
jmethodID get = env->GetStaticMethodID(cls, "Apple", "()V");
env->CallStaticVoidMethod(cls,get);
局部引用和全局引用
当我们执行两次同一个方法时。例如下面这个,如果没有将这个clss变成全局引用(即if语句的第二行)则第二次运行时则会报错。原因是:第一次运行时,clss赋值了,结束后,指针被(jclss)释放。但是不会赋空值。所以不会执行if内部代码。
jclass clss;
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jnitwo_MainActivity_test2(JNIEnv *env, jobject thiz, jintArray number) {
if(clss==NULL){
jclass cls = env->FindClass("com/example/jnitwo/Bean");
//将jclss变成全局引用
clss= static_cast<jclass>(env->NewGlobalRef(cls));
//释放局部引用
env->DeleteLocalRef(cls);
}
jmethodID get = env->GetStaticMethodID(clss, "Apple", "()V");
env->CallStaticVoidMethod(clss,get);
}
错误例子:
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jnitwo_MainActivity_test2(JNIEnv *env, jobject thiz, jintArray number) {
jclass cls;
if(cls==NULL){
jclass cls = env->FindClass("com/example/jnitwo/Bean");
}
jmethodID get = env->GetStaticMethodID(clss, "Apple", "()V");
env->CallStaticVoidMethod(clss,get);
}
如果要将全局引用释放:
//释放全局引用
env->DeleteGlobalRef(clss);
弱全局引用:不会阻止被回收
问题:当我们使用弱引用的时候,引用的对象可能被回收了
所以我们在使用的时候则需要判断是否被回收。
jclass a;
jclass clss;
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jnitwo_MainActivity_test2(JNIEnv *env, jobject thiz, jintArray number) {
jclass a;
a = env->FindClass("com/example/jnitwo/Bean");
::a= static_cast<jclass>(env->NewWeakGlobalRef(a));
//判断是否被释放
//如果返回true:被释放了。false:未被释放
jboolean i = env->IsSameObject(::a, NULL);
}