NDK之旅必需要知道的一些基本知识

JNIEnv

  • 定义: 至关于一个jni上下文对象。java

  • 做用: 经过JNIEnv的指针可以对Java端的代码进行操做:编程

    • a.建立Java对象.数组

      jstring str = (env).NewStringUTF("终端研发部");
      jclass jclazz = (
      env).GetObjectClass(obj);缓存

      • b.调用Java对象的方法。bash

        jclz = (*env)->FindClass(env, "java/lang/String");
        jdouble doub = (*env).GetStaticDoubleField()复制代码
      • c.获取及设置Java对象的属性。微信

        jintArray arrayResult = NULL;
        jclass  jclazz = (*env).GetObjectClass(obj);
        jint * elements =(*env).GetIntArrayElements(array,NULL);复制代码

        Get/Set[Static] Method jvm

        JNI中一般用JType指代Java环境中的类。

typedef _jobject *jobject;  
typedef _jclass *jclass;  
typedef _jthrowable *jthrowable;  
typedef _jstring *jstring;  
typedef _jarray *jarray;  
typedef _jbooleanArray *jbooleanArray;  
typedef _jbyteArray *jbyteArray;  
typedef _jcharArray *jcharArray;  
typedef _jshortArray *jshortArray;  
typedef _jintArray *jintArray;  
typedef _jlongArray *jlongArray;  
typedef _jfloatArray *jfloatArray;  
typedef _jdoubleArray *jdoubleArray;  
typedef _jobjectArray *jobjectArray;复制代码

JType都继承自JObject

[cpp] view plain copy
class _jobject {};  
class _jclass : public _jobject {};  
class _jthrowable : public _jobject {};  
class _jstring : public _jobject {};  
class _jarray : public _jobject {};  
class _jbooleanArray : public _jarray {};  
class _jbyteArray : public _jarray {};  
class _jcharArray : public _jarray {};  
class _jshortArray : public _jarray {};  
class _jintArray : public _jarray {};  
class _jlongArray : public _jarray {};  
class _jfloatArray : public _jarray {};  
class _jdoubleArray : public _jarray {};  
class _jobjectArray : public _jarray {};复制代码

jobject的理解

JNIEXPORT void JNICALL Java_com_jue_testnative_TestNative1_hello(JNIEnv *, jobject);

这里是jobject指代的在Java中调用native方法的java类实例复制代码

获取jclass的方法

a. jclass FindClass(const char *name)

b. jclass GetObjectClass(jobject obj)复制代码

FindClass注意事项:

  • 注意: 会在ClassPath下面寻找类。须要传入完整类名,注意包与包之间用’/'。函数

    jclass class_str = env->FindClass("java/lang/String");复制代码

jfiledID/jmethodID的获取

  • 在natvie方法中获取/设置字段的值,或者方法调用,须要先获取相应的field/method的ID工具

    • jfieldID GetFieldID(jclass clazz, const char name, const char sig) ui

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

注意sig用来处理函数重载引发的不肯定。

而后经过ID获取

jobject GetObjectField(jobject obj, jfieldID fieldID)   

jobject CallObjectMethod(jobject obj, jmethodID methodID, ...)  复制代码

获取方法的签名

  • javap -s 命令工具能够查看一个类的方法的签名

    javap -s xxx.class

Sin签名含义细节

类型 相应的签名
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
String Ljava/lang/String
Array [Ljava/lang/Object
Method (para s1,para s2) 返回值签名

jni如何调用Java里面的方法的

NIEnv提供了一下函函数(可以实现子类对象调用父类方法的功能)

  • Call Method,
  • CallStatic Method,
  • CallNonvirtual Method。

    CallVoidMethod(jobject obj, jmethodID methodID, ...)
    
      void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
    
      void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)复制代码

    调用实例方法的三种方法

  • Call Method(jobject obj, jmethodID id, ...);

    boolean funcation(int i, double d, char c) {  
      }  
    
      env->CallBooleanMethod( obj, id_function , 100L, 3.44, L'3');复制代码
  • Call MethodV(jobject obj, jmethodID id,va_list lst);

  • Call MethodA(jobject obj, jmethodID id, jvalue* v);
    ```
    jvalue是一个联合体

    [cpp] view plain copy
    typedef union jvalue {

    jboolean z;  
      jbyte    b;  
      jchar    c;  
      jshort   s;  
      jint     i;  
      jlong    j;  
      jfloat   f;  
      jdouble  d;  
      jobject  l;  复制代码

    } jvalue;

jvalue *args = new jvalue[3];  
args[0].i = 100L;  
args[1].d = 3.44;  
args[2].c = L'3';  
env->CallBooleanMethodA(obj, id_funcation, args);  
delete [] args;      
```复制代码

Java对象的建立

  • 方式-NewObject

使用NewObject来建立Java对象。

jobject NewObject(jclass clazz, jmethodID methodID, ...)复制代码

须要先活的相应构造器的name,方法名设定为 ,另外返回值的签名是Void

jmethodID GetMethodID(jclass clazz, const char *name, const char *sig)复制代码

例子:

jclass class_date = env->FindClass("java/util/Date");  
jmethodID method_id = env->GetMethodID(class_date,"<init>","()V");  
jobject now = env->NewObject(class_date, method_id);复制代码
  • b.方式-AllocObject
    ```
    jclass cls = (env).GetObjectClass(obj);
    jobject oobj = (
    env).AllocObject(cls);
#### Java字符串和C/C++字符串之间的转换

-  a.在java中字符串String对象是Unicode(UTF-16)码,每一个字符不管中文仍是英文都占用两个字节。

- b.能够经过JNI接口把Java中的字符串转换成C/C++中的宽字符串,Java也能够传一个UTF-8的字符串(char*) 到C/C++中。

- c.反过来C++能够经过把一个宽字符串,或者一个UTF-8的字符串来建立一个Java端的对象。

#### memset的注意事项
- C库函数 void *memset(void *str, int c, size_t n) 复制字符c(unsigned char类型)参数str指向的字符串的前n个字符。

- memset是以字节为单位,初始化内存块。
当初始化一个字节单位的数组时,能够用memset把每一个数组单元初始化成任何你想要的值

    `复制代码
char data[10];  
memset(data, 1, sizeof(data));    // right  
memset(data, 0, sizeof(data)); 

char str[50];
strcpy(str,"This is string.h library function");
puts(str);
memset(str,'$',7);
puts(str);

```复制代码
  • 在初始化其余基础类型时,则须要注意,好比
int data[10];  
memset(data, 0, sizeof(data));    // right  
memset(data, -1, sizeof(data));    // right  
memset(data, 1, sizeof(data));    // wrong, data[x] would be 0x0101 instead of 1复制代码

获取字符串

  • a.取得与某个jstring对象相关的Java字符串
方法 |     做用
---|---
GetStringChars |     取得UTF-16编码的宽字符串(char*)
GetStringUTFChars |     取得UTF-8编码的字符串(char*)复制代码

注意在不使用到的使用,要注意使用ReleaseStringChars,或者ReleaseStringUTFChars释放拷贝的内存,或Java对象的引用。

还可使用下面的方法,这个方法能够增长返回jvm中java对象的可能性,可是仍是有可能返回相应java串的拷贝。

这个方法没有相应的GetStringUTFCritical,因为Java字符串使用的是UTF-16,要转换成UTF-8编码的串原本就须要一次拷贝动做。

数组分为2种

  • a.基本类型的数组。

  • b.对象类型(Object[])的数组。

    有一个通用于两种不一样数组的的函数:获得数据的长度函数:GetArrayLength

处理基本类型数组

跟处理字符串类似

Get ArrayElements,能够把Java基本类型的数组转换成C/C++中的数组,能够拷贝一份传本地代码,也能够把指向Java中的指针直接传回本地代码。

须要用

Release ArrayElements

void ReleaseIntArrayElements(jintArray array,  
                             jint *elems,  
                             jint mode) {  
    functions->ReleaseIntArrayElements(this,array,elems,mode);  
}复制代码
mode的类型:
  • 0:对Java数组进行更新,并释放C/C++的数组。

  • JNI_COMMIT: 对Java数组进行更新但不释放C/C++数组。

  • JNI_ABORT:对Java数组不进行更新,并释放C/C++数组。

类似GetStringUTFCritical的函数:为了直接传回指向Java数组的指针而加入的函数。

void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) {  
    return functions->GetPrimitiveArrayCritical(this,array,isCopy);  
}  
void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) {  
    functions->ReleasePrimitiveArrayCritical(this,array,carray,mode);  
}复制代码

Get ArrayRegion 类似于GetStringRegion的函数:没有Release方法,由于咱们是拷贝

void GetIntArrayRegion(jintArray array,  
                       jsize start, jsize len, jint *buf) {  
    functions->GetIntArrayRegion(this,array,start,len,buf);  
}复制代码

Set ArrayRegion 能够把指定范围内的Java数组元素用C++中的元素赋值。

void SetIntArrayRegion(jintArray array, jsize start, jsize len,  
                       const jint *buf) {  
    functions->SetIntArrayRegion(this,array,start,len,buf);  
}复制代码

建立一个基本类型的Java数组

Array New Array(jsize size),指定长度后返回相应Java基本类型的数组。

处理Object类型数组

JNI中没有把Java对象类型的数组转换成C++中的jobject[]的函数。而是直接经过Get/SetObjectArrayElement来对Java的Object数组进行操做。

jobject GetObjectArrayElement(jobjectArray array, jsize index) {  
    return functions->GetObjectArrayElement(this,array,index);  
}  
void SetObjectArrayElement(jobjectArray array, jsize index,  
                           jobject val) {  
    functions->SetObjectArrayElement(this,array,index,val);  
}复制代码

使用上述方式不须要释放资源。
能够根据数组长度和初始值来建立某个类的数组

jobjectArray NewObjectArray(jsize len, jclass clazz,  
                            jobject init) {  
    return functions->NewObjectArray(this,len,clazz,init);  
}复制代码

java对象在JNI中的引用

在jvm中建立的对象被传到本地的C/C++代码的时候,会产生引用。根据jvm的垃圾回收机制,只要引用存在,就不会触发该引用指向对象的被回收。

这些引用分为三种

  • 局部引用Local Reference:是最多见的引用,局部引用只在native函数中有效。局部引用的存在会阻止其指向对象的回收。

  • 全局引用Global Reference:

    • 能够跨越多个线程

    • 在多个navtive方法中有效、

    • 全局引用存在期间会阻止其引用java对象在jvm的回收。

    • 全局引用的建立不是JNI自动建立的。

    • 全局引用的建立要显示的调用NewGlobalRef函数,而释放须要调用ReleaseGlobalRef

  • 弱全局引用:

    • 与全局引用类似,建立和释放都须要由编程人员来进行。

    • 多个线程,多个native方法中有效。

      区别:这个引用不会阻止jvm回收其指向的引用。

      NewWeakGlobalRef与ReleaseWeakGlobalRef来建立和释放引用。

IsSameObject

IsSameObject在弱全局引用中有一个特别的功能。

把NULL传给要判断的object,来判断弱全局引用指向的java对象是否被回收。

缓存jfieldID,jmethodID.

  • a.经过方法名+签名来查询jfieldID,jmethod开销是很是大的。

  • b.缓存方式:

    • 1.在使用的时候缓存。(Caching at the Point of Use)

      使用static类型的局部变量来保存已经查询过的ID,这样就不会在每次调用的时候查询,而是只查询一次。

    • 2.在java类初始化的时候缓存。(Caching at Class's inititalizer)

      这种加载方式在jvm类的加载和从新加载都会从新呼叫该native方法从新计算ID

public class TestNative  
{  
    static {  
        initNativeIDs();  
    }  
    static natvie void initNativeIDs();  
    int propInt = 0;  
}复制代码
JNIEXPORT void JNICALL    Java_TestNative_initNativeIDs(JNIEnv *env, jobject object)  
{  
    ......  
    g_propInt_id = GetFieldID(clazz,  "propInt", "I" );  

}复制代码

相信本身,没有作不到的,只有想不到的

若是你以为此文对您有所帮助,欢迎入群 QQ交流群 :644196190
微信公众号:终端研发部

技术+职场
技术+职场
相关文章
相关标签/搜索