jni调用java静态方法_Android JNI开发系列(九)JNI调用Java的静态方法&实例方法

JNI调用Java的静态方法&实例方法

package org.professor.jni.bean;

import android.util.Log;

/**

* Created by peng on 2018/10/11.

*/

public class Person {

/*C/CPP 调用Java 静态方法 */

public native void callJavaStaticMethod();

/*C/C++ 调用Java 实例方法 */

public native void callJavaInstanceMethod();

public static void setPersonInfo(String name, int age) { Log.i("PERSON", "name= " + name + "\\t age=" + age); }

public void setPersonMoney(float money) { Log.i("PERSON", "money= " + money);

}

}

上面写了一个Java Bean类,里面定义了两个Native方法,分别用来调用,该类的静态方法和实例方法,实如今本地native方法里java

JNI调用静态方法

# include

# include

# include

//extern "C" // C 编译器编译个人代码

JNIEXPORT void JNICALL Java_org_professor_jni_bean_Person_callJavaStaticMethod(JNIEnv *env, jobject instance) {

//1.获取类类型的Class对象

jclass personClass = (*env)->FindClass(env, "org/professor/jni/Person");

if (NULL == personClass) {

__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND CLASS");

return;

}

//2.获取方法ID 方法签名:(String:Ljava/lang/String ;int:I) 方法签名能够经过javap -s命令生成

jmethodID pJmethodID = (*env)->GetStaticMethodID(env, personClass, "setPersonInfo",

"(Ljava/lang/String;I)V");

if (NULL == pJmethodID) {

__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND STATIC METHOD");

return;

}

jstring str = (*env)->NewStringUTF(env, "Yimi");

//3.调用方法

(*env)->CallStaticVoidMethod(env, personClass, pJmethodID, str, 18);

// 删除局部变量引用表,JVM内部因为引用表,

// 记录全局和局部的变量,当超过引用表数量,致使内存溢出

// 形成崩溃

(*env)->DeleteLocalRef(env, personClass);

(*env)->DeleteLocalRef(env, str);

}

注意:android

JVM 针对全部数据类型的返回值都定义了相关的函数。上面callStaticMethod 方法的返回类型为 void,因此调用 CallStaticVoidMethod。根据返回值类型不一样,JNI 提供了一系列不一样返回值的函数,如:CallStaticIntMethod、CallStaticFloatMethod、CallStaticShortMethod、CallStaticObjectMethod等,分别表示调用返回值为 int、float、short、Object 类型的函数,引用类型统一调用CallStaticObjectMethod 函数。另外,每种返回值类型的函数都提供了接收3种实参类型的实现:CallStaticXXXMethod(env, clazz, methodID, ...),CallStaticXXXMethodV(env, clazz, methodID, va_list args),CallStaticXXXMethodA(env, clazz, methodID, const jvalue args),分别表示:接收可变参数列表、接收 va_list 做为实参和接收const jvalue为实参。下面是jni.h头文件中CallStaticVoidMethod 的三种实参的函数原型:安全 void (JNICALL *CallStaticVoidMethod) (JNIEnv *env, jclass cls, jmethodID methodID, ...);

void (JNICALL *CallStaticVoidMethodV) (JNIEnv *env, jclass cls, jmethodID methodID, va_list args);

void (JNICALL *CallStaticVoidMethodA) (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args);

虽然函数结束后,JVM 会自动释放全部局部引用变量所占的内存空间。但仍是手动释放一下比较安全,由于在 JVM 中维护着一个引用表,用于存储局部和全局引用变量,经测试在 Android NDK 环境下,这个表的最大存储空间是512 个引用,若是超过这个数就会形成引用表溢出,JVM 崩溃。在 PC 环境下测试,无论申请多少局部引用也不释放都不会崩,我猜可能与 JVM 和 Android Dalvik 虚拟机实现方式不同的缘由。因此有申请就及时释放是一个好的习惯!(局部引用和全局引用在后面的文章中会详细介绍)函数

JNI调用实例方法

JNIEXPORT void JNICALL Java_org_professor_jni_bean_Person_callJavaInstanceMethod(JNIEnv *env, jobject instance) {

//1.获取类类型的Class对象

jclass personClass = (*env)->FindClass(env, "org/professor/jni/Person");

if (NULL == personClass) {

__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND CLASS");

return;

}

//2.获取方法ID 方法签名:(String:Ljava/lang/String ;int:I)

jmethodID pJmethodID = (*env)->GetMethodID(env, personClass, "setPersonMoney",

"(F)V");

if (NULL == pJmethodID) {

__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND INSTANCE METHOD");

return;

}

//3.获取默认的构造方法ID (; = public org.professor.jni.bean.Person(); ),先获取构造函数的ID

jmethodID constructor = (*env)->GetMethodID(env, personClass, "", "()V");

if (NULL == constructor) {

__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND INSTANCE CONSTRUCTOR METHOD");

return;

}

//4.获取默认构造函数对象

jobject object = (*env)->NewObject(env, personClass, constructor);

if (NULL == object) {

__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND OBJECT");

return;

}

//5.调用实例方法

(*env)->CallVoidMethod(env, object, pJmethodID, 30.0);

//6.删除局部引用变量

(*env)->DeleteLocalRef(env, personClass);

(*env)->DeleteLocalRef(env, object);

}

扩展方法签名表

签名

java类型

V

void

Z

boolean

I

int

J

long

D

double

F

float

B

byte

C

char

S

short

[I

int[]

[F

float[]

[B

byte[]

[C

char[]

[S

short[]

[D

double[]

[J

long[]

[Z

boolean[]

L用/分割包的完整类名; Ljava/lang/String;

Object

例如: void set(String str); 签名:"(Ljava/lang/String;)V"测试

能够用javap -s "ClassFile" 命令查看方法签名spa

总结

调用静态方法使用 CallStaticXXXMethod/V/A 函数,XXX 表明返回值的数据类型。如:CallStaticIntMethod

调用实例方法使用 CallXXXMethod/V/A 函数,XXX 表明返回的数据类型,如:`CallIntMethod

获取一个实例方法的 ID,使用 GetMethodID 函数,传入方法名称和方法签名

获以一个静态方法的 ID,使用 GetStaticMethodID 函数,传入方法名称和方法签名

获取构造方法 ID,方法名称使用""

获取一个类的 Class 实例,使用 FindClass 函数,传入类描述符。JVM 会从 classpath 目录下开始搜索。

建立一个类的实例,使用 NewObject 函数,传入 Class 引用和构造方法 ID

删除局部变量引用,使用 DeleteLocalRef, 传入引用变量

方法签名格式:(形参参数列表)返回值类型。注意:形参参数列表之间不须要用空格或其它字符分隔

类描述符格式:L 包名路径/类名;,包名之间用/分隔。如:Ljava/lang/String;

调用 GetMethodID 获取方法 ID 和调用 FindClass 获取 Class 实例后,要作异常判断

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值