感触:楼主学习JNI有几天了,发现网上点击率非常高的博客里面错误百出,让我这个NDK小白费了很多功夫才把一个个案例跑起来,决定自己写一篇博客记录一下也能给需要的人一些帮助。
JNI 最常用的Signautre的查找方式:
AS->Terminal->javap -s -p [类的包地址]
如果是AS项目中的类需要cd 到app\build\intermediates\classes\debug下再执行上面的代码。
如果是JDK的类就直接整。
eg:javap -s -p java.util.ArrayList
-------------结果-------------------------------
public java.util.ArrayList(int);
Signature: (I)V
public java.util.ArrayList();
Signature: ()V
public java.util.ArrayList(java.util.Collection<? extends E>);
Signature: (Ljava/util/Collection;)V
public boolean add(E);
Signature: (Ljava/lang/Object;)Z
-------------------------------------------------------------------------------
下面开始常用案例的总结:(版本:AS1.5 NDK r10e)
1.Native返回ArrayList<E>
需要用到的方法:
1.FindClass:传入一个Class描述符,JVM会从classpath路径下搜索该类,并返回jclass类型(用于存储Class对象的引用)。注意ClassMethod的Class描述符为com/study/jnilearn/ClassMethod,要将.(点)全部换成/(反斜杠)
最重要的是在这个路径下不要加L和分号否则AS必然报错!
2.GetMethodID:调用GetMethodID函数,从ArrayList类中获取构造函数方法ID,返回jmethodID类型(用于存储方法的引用)。实参clazz是第一步找到的jclass对象,实参"callStaticMethod"为方法名称,实参“(Ljava/lang/String;I)V”为方法的签名。
eg:
<span style="font-size:18px;">mid_instance = (env)->GetMethodID( clazz, "callInstanceMethod","(Ljava/lang/String;I)V");
此方法对应:
private void callInstanceMethod(String str, int i) </span>
3.NewObject:构造新的 Java 对象。方法 ID指示应调用的构造函数方法。
4.NewStringUTF:通过调用NewStringUTF函数,会构建一个新的java.lang.String字符串对象。这个新创建的字符串会自动转换成Java支持的Unicode编码。如果JVM不能为构造java.lang.String分配足够的内存,NewStringUTF会抛出一个OutOfMemoryError异常,并返回NULL。在这个例子中我们不必检查它的返回值,如果NewStringUTF创建java.lang.String失败,OutOfMemoryError这个异常会被在Sample.main方法中抛出。如果NewStringUTF创建java.lang.String成功,则返回一个JNI引用,这个引用指向新创建的java.lang.String对象。
5.CallXXXMethod:
JVM针对所有数据类型的返回值都定义了相关的函数,JNI提供了一系列不同返回值的函数,如:CallIntMethod、CallFloatMethod、CallShortMethod、CallObjectMethod等,分别表示调用返回值为int、float、short、Object类型的函数,引用类型统一调用CallObjectMethod函数。另外,每种返回值类型的函数都提供了接收3种实参类型的实现:CallXXXMethod(env, clazz, methodID, ...),CallXXXMethodV(env, clazz, methodID, va_list args),CallXXXMethodA(env, clazz, methodID, const jvalue *args),分别表示:接收可变参数列表、接收va_list作为实参和接收const jvalue*为实参。
献上作者编译好能运行的代码
Native层:
JNIEXPORT jobject JNICALL
Java_c_example_com_jni_jnidemo_getPeopleInfo(JNIEnv *env, jclass cls, jobject students) {
//获得ArrayList类引用 记住FindClass千万不能后面跟分号。。。
jclass list_cls = env->FindClass("java/util/ArrayList");
//获得得构造函数Id
jmethodID list_costruct = env->GetMethodID(list_cls, "<init>", "()V");
//创建一个Arraylist集合对象
jobject list_obj = env->NewObject(list_cls, list_costruct);
if(list_cls==NULL)
return NULL;
//获得add方法 后面的签名
jmethodID list_add = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z");
//获取People类
jclass peo_cls = env->FindClass("c/example/com/jni/People");
//获取People类的构造函数 参数:String Student
jmethodID peo_costruct = env->GetMethodID(peo_cls , "<init>", "(Ljava/lang/String;Lc/example/com/jni/Student;)V");
//获取Student类
jclass stu_cls=env->FindClass("c/example/com/jni/Student");
//获取Student构造函数
jmethodID stu_construct=env->GetMethodID(stu_cls, "<init>", "(Ljava/lang/String;I)V");
for(int i = 0 ; i < 3 ; i++)
{
//构造java中的name
jstring str = env->NewStringUTF("ssssss");
//构造一个Student对象
jobject stu_obj = env->NewObject(stu_cls, stu_construct, str, 11);
//构造一个People对象
jobject peo_obj = env->NewObject(peo_cls , peo_costruct , str,stu_obj);
//执行Arraylist类实例的add方法,添加一个People对象
env->CallBooleanMethod(list_obj , list_add , peo_obj);
}
return list_obj;
}
Java层:
public static native ArrayList<People> getPeopleInfo(ArrayList<Student> students);
Bean层:
public class People {
private String name;
private Student student;
public People(){}
public People(String name, Student student) {
this.name = name;
this.student = student;
}
}
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
常用签名:
类型 相应的签名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V
object L用/分隔包的完整类名: Ljava/lang/String;
Array [签名 [I [Ljava/lang/Object;
Method (参数1类型签名 参数2类型签名···)返回值类型签名
特别注意:Object后面一定有分号(;)结束的,多个对象参数中间也用分号(;)来分隔