[android jni]android JNI 详细介绍

来源:互联网 发布:js数组去掉空格 编辑:程序博客网 时间:2024/05/17 20:27

主题:

●Java和JNI之间的数据对应关系

●数组操作

●相关数据处理函数,如字符串的创建,运算,转换

 

1. Java和JNI之间的数据对应关系

很多人都很疑惑为什么要定义数的数据类型呢,为什么不延用Java中的定义呢,

有这个疑惑的童鞋是平时没有注意数据类型大小的定义,Android这么做目的是因为Java中的基本数据类型的字节长度,并不是根据cpu的运算能力来定义的,如int在java中永远是4字节长,所以android这么做是为了兼容java的定义。

android jni 数据类型都定义在jni.h中的,我们贴出来方便大家平时的检阅:

[cpp] view plaincopy
  1. typedef uint8_t         jboolean;       /* unsigned 8 bits */  
  2. typedef int8_t          jbyte;          /* signed 8 bits */  
  3. typedef uint16_t        jchar;          /* unsigned 16 bits */  
  4. typedef int16_t         jshort;         /* signed 16 bits */  
  5. typedef int32_t         jint;           /* signed 32 bits */  
  6. typedef int64_t         jlong;          /* signed 64 bits */  
  7. typedef float           jfloat;         /* 32-bit IEEE 754 */  
  8. typedef double          jdouble;        /* 64-bit IEEE 754 */  
[cpp] view plaincopy
  1. typedef jint            jsize;  
[cpp] view plaincopy
  1. class _jobject {};  
  2. class _jclass : public _jobject {};  
  3. class _jstring : public _jobject {};  
  4. class _jarray : public _jobject {};  
  5. class _jobjectArray : public _jarray {};  
  6. class _jbooleanArray : public _jarray {};  
  7. class _jbyteArray : public _jarray {};  
  8. class _jcharArray : public _jarray {};  
  9. class _jshortArray : public _jarray {};  
  10. class _jintArray : public _jarray {};  
  11. class _jlongArray : public _jarray {};  
  12. class _jfloatArray : public _jarray {};  
  13. class _jdoubleArray : public _jarray {};  
  14. class _jthrowable : public _jobject {};  
  15.   
  16. typedef _jobject*       jobject;  
  17. typedef _jclass*        jclass;  
  18. typedef _jstring*       jstring;  
  19. typedef _jarray*        jarray;  
  20. typedef _jobjectArray*  jobjectArray;  
  21. typedef _jbooleanArray* jbooleanArray;  
  22. typedef _jbyteArray*    jbyteArray;  
  23. typedef _jcharArray*    jcharArray;  
  24. typedef _jshortArray*   jshortArray;  
  25. typedef _jintArray*     jintArray;  
  26. typedef _jlongArray*    jlongArray;  
  27. typedef _jfloatArray*   jfloatArray;  
  28. typedef _jdoubleArray*  jdoubleArray;  
  29. typedef _jthrowable*    jthrowable;  
  30. typedef _jobject*       jweak;  
[cpp] view plaincopy
  1. typedef union jvalue {  
  2.     jboolean    z;  
  3.     jbyte       b;  
  4.     jchar       c;  
  5.     jshort      s;  
  6.     jint        i;  
  7.     jlong       j;  
  8.     jfloat      f;  
  9.     jdouble     d;  
  10.     jobject     l;  
  11. } jvalue;  

 

2. 数组操作

1)获取长度

jarray array;
env->GetArrayLength(array)

2)访问数组元素

这个要取决于数组是对象还是基本数据类型。

对象数组:

jobjectArray array = ... ;

int i, j;

jobject object = env->GetObjectArrayElement(array, i);

env->GetObjectArrayElement(array, j, x);

基本数据:

xxx* a = env->GetxxxArrayElements(array, null) //把xxx换成double, int, boolean等等

 然后可以a[i]来访问

用完以后一定要用下面的方法去Release, 这样才会把修改写回实现的地址。

env->ReleasexxxArrayElements(array, a, 0);

大范围数组元素操作

void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) //buf用来返回数组段的起始地址

void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean* buf)

3)创建Java数组

jobjectArray array = env->NewObjectArray(100, clazz, NULL); // 100表示长,clazz java类对象, NULL类对象

jdoubleArray array = env->NewDoubleArray(100); //创建基本数据类型数组

3. 字符串操作

  jstring NewString(const jchar* unicodeChars, jsize len)
 

 jsize GetStringLength(jstring string)

  const jchar* GetStringChars(jstring string, jboolean* isCopy)

  void ReleaseStringChars(jstring string, const jchar* chars)

  jstring NewStringUTF(const char* bytes)

  jsize GetStringUTFLength(jstring string)

  const char* GetStringUTFChars(jstring string, jboolean* isCopy)

  void ReleaseStringUTFChars(jstring string, const char* utf)

 

主题:

● 获取Java类

● 访问Java属性

●访问Java方法

●创建Java对象

●抛异常

 

1. 获取Java类

    方法一:

    jclass clazz = env->GetObjectClass(thisObj);

    方法二:

    jclass cls = env->FindClass("com/lht/JNITest");

以上方法是先获取到Class对象,当然这是c++的写法,c的写法是不一定的,我是喜欢用c++的方式来实现JNI

 

2. 读写Java属性

以后是用到的JNI 函数

 

[cpp] view plaincopy
  1. jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);  
  2.   
  3. jobject     (*GetObjectField)(JNIEnv*, jobject, jfieldID);  
  4. jboolean    (*GetBooleanField)(JNIEnv*, jobject, jfieldID);  
  5. jbyte       (*GetByteField)(JNIEnv*, jobject, jfieldID);  
  6. jchar       (*GetCharField)(JNIEnv*, jobject, jfieldID);  
  7. jshort      (*GetShortField)(JNIEnv*, jobject, jfieldID);  
  8. jint        (*GetIntField)(JNIEnv*, jobject, jfieldID);  
  9. jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);  
  10. jfloat      (*GetFloatField)(JNIEnv*, jobject, jfieldID);  
  11. jdouble     (*GetDoubleField)(JNIEnv*, jobject, jfieldID);  
  12.   
  13. void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);  
  14. void        (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);  
  15. void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);  
  16. void        (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);  
  17. void        (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);  
  18. void        (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);  
  19. void        (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);  
  20. void        (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);  
  21. void        (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);  

 

我们先看一下

jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);//这个方法相当的耗时,请不要频繁的使用,当读到以后应该保存起来

JNIEnv*就不用说了,jclass是要访问属性的类的class对象,如何获取已在上一节讲述了,第三个参数为属性名称,如你定义了一个String mName;那么参数为"mName",

第四个参数是属性的签名,mName的类型为String,那么它的签名应该是指"Ljava/lang/String;"

所以我们代码写成:jfieldID nameId = env->GetFieldID(class, "mName", "Ljava/lang/String;")

拿到nameId我们再来看看

jString name = (jString)env->GetObjectField(jObj, nameId);

jObj是java调用传入的this对象。

如果属性域是静态的,那么方法为GetStaticFieldID, GetStaticObjectField

获取完了以后,我们要设置呢?

env->SetObjectField(jObj, nameId, name); //第三个参数为值

 

3. 访问Java方法

根读写java属性的步骤一致。

先获取方法的ID

jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)

jclass, 就是要访问的类的类对象,是需要根据本篇的第一节的描述获取的jclass对象

name, 方法名称

sig, 方法的签名,如:“(Ljava/lang/String;I)V”

CallVoidMethod(jclass clazz,, methodID, ...);

CallVoidMethod(jclass clazz,, methodID, jvalue args[]);

CallVoidMethod(jclass clazz,, methodID, va_list args);

... ,  用来传递参数,这个相信在Java中用的比较的多,如println类,你可以在后面跟很多个参数,如jString,jint and so on.

jvalue是一个枚举类:

typedef union jvalue {
    jboolean    z;
    jbyte       b;
    jchar       c;
    jshort      s;
    jint        i;
    jlong       j;
    jfloat      f;
    jdouble     d;
    jobject     l;
} jvalue;

va_list,是c语言中用来解决变参的宏,

后面两个调用方法不用管,一般我们用第一个就够了。

调用静态的方法:

跟上面的没有什么区别,只是分别用GetStaticMethodID和CallStaticVoidMethod方法

CallStaticObjectMethod(class, methodid, env->newStringUTF("java.class.path"))

 

4. 在JNI中创建Java类对象

jobject NewObject(jclass clazz, jmethodID methodID, ...)

参数说明:

clazz, java类

jmethodID, 构造函数ID

..., 构造参数

 

5. 抛异常

有两个接口可以实现

jint        (*Throw)(JNIEnv*, jthrowable);
jint        (*ThrowNew)(JNIEnv *, jclass, const char *);

 

6. 创建Java虚拟机

在非JNI的代码中,我们可以通过创建一个Java虚拟机来访问Java类

JavaVMOption options[1];

JavaVMInitArgs vm_args;

JavaVM *jvm;

JNIEnv *env;

 

options[0].optionString = "-Djava.class.path=.";

memset(&vm_args, 0, sizeof(vm_args));

vm_args.version = JNI_VERSION_1_4;

vm_args.nOption = 1;

vm_args.options = options;

JNI_CreateJavaVM(&jvm, (void**) & env, &vm_args);

 

●编码签名

BbyteCcharDdoubleFfloatJlongLclassname;类的类型SshortVvoidZbooleanIint

例如:

void Employee(java.lang.String, double, java.util.Date)

具有如下签名:

"(Ljava/lang/String;DLjava/util/Date;)V"

需要说明的是以上的分号并不是参数分隔符

数组则需要在前后加“[”

如:

float[]    [F

float[][]  [[F

 

当然我们也可以不用自己去写这个签名

javap -s -private Employee,可以自动生成签名

 

0 0