android jni 常用的方法

NI的基本类型

在JNI中有一些基本类型,这些基本类型只能在JNI层使用

序号属性名java层对应的类型
1jobjectObject
2jbooleanboolean
3jbytebyte
4jcharchar
5jshortshort
6jintObject
7jlonglong
8jfloatfloat
9jdoubledouble
10voidvoid

由于下面的函数名都会包含这些基本属性,我这这里标记为<type>,代表了以上的类型

java类型的签名

序号属性名java层对应的类型
1Zboolean
2Bbyte
3Cchar
4Sshort
5Iint
6Jlong
7Ffloat
8Dboolean
9L
10[Btype[]

关于变量的操作

序号方法名备注
1jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)根据变量名以及变量签名获得参数ID
2jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)根据变量名以及变量签名获得静态参数ID
3<type> Get<type>Field(jobject obj, jfieldID fieldID)根据fieldID获取变量
4void Set<type>Field(jobject obj, jfieldID fieldID, <type>value)根据fieldID设置变量值为value
5<type> GetStatic<type>Field(jobject obj, jfieldID fieldID)根据fieldID获取静态变量<type>
6void SetStatic<type>Field(jobject obj, jfieldID fieldID, <type>value)根据fieldID设置静态变量值为value

类的操作

序号方法名备注
1jclass FindClass(JNIEnv *env, const char *name);name:类的全名。使用UTF-8编码,其中分隔符使用/表示。return:java类对象。发生错误时返回NULL。
2jclass GetSuperclass(JNIEnv *env, jclass clazz);clazz:需要查询的类对象。return:clazz的父类类对象。如果clazz是Object类对象或者clazz是接口的类信息,返回NULL。
3jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1,jclass clazz2);当我们将一个对象从一种类型转换为另一种类型之前,我们必须确保这种类型转换是安全的。我们可以通过如下方法去判断这两种类型是否可以互相转换。 clazz1:原始类型。class2:目标类型。return:当前类型转换是否安全。
4jclass DefineClass(JNIEnv *env, const char *name, jobject loader,const jbyte *buf, jsize bufLen);name:类的全名,必须是被UTF-8编码过的字符串。loader:类加载器buf:包含类数据的缓冲区。这个缓冲区包含一个原始类数据,并且要求这个类在调用此方法前没有被JVM所引用。bufLen:缓冲区长度。return:java类对象。发生错误时返回NULL。

创建对象的基本操作

序号方法名备注
1jobject AllocObject(JNIEnv *env, jclass clazz);返回使用clazz类创建的对象,如果clazz没有默认的构造方法,则返回NULL。
2jobject NewObject(JNIEnv *env, jclass clazz,jmethodID methodID, …);...代表着传入构造参数
3jobject NewObjectA(JNIEnv *env, jclass clazz,jmethodID methodID, const jvalue *args);args:这里需要传入参数数组
4jobject NewObjectV(JNIEnv *env, jclass clazz,jmethodID methodID, va_list args);args:指向变参列表的指针
5jclass GetObjectClass(JNIEnv *env, jobject obj);通过java层传过来的对象创建jObject
6jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);通过如下方法可以得到当前对象的引用类型是全局引用、局部引用还是弱全局引用。

typedef enum jobjectRefType {
    JNIInvalidRefType = 0,//无效引用
    JNILocalRefType = 1,//局部引用
    JNIGlobalRefType = 2,//全局引用
    JNIWeakGlobalRefType = 3//弱全局引用
} jobjectRefType;

其他关于对象的一些方法

序号方法名备注
1jboolean IsInstanceOf(JNIEnv *env, jobject obj,jclass clazz);在Java中我们使用instanceof来判断一个对象是否是一个类的实例,在JNI中,需要通过如下方法来进行实例运算。
2jboolean IsSameObject(JNIEnv *env, jobject ref1,jobject ref2);在Java中使用==可以判断两个引用是否指向同一个对象,在JNI中使用下列方法可以进行相同的判断,无论是全局引用,局部引用还是弱全局引用。

关于方法的操作

序号方法名备注
1jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);根据方法名以及方法签名获得方法ID
2jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)根据方法名以及方法签名获得静态方法ID
3<type> Call<type>Method(jobject obj, jmethodID methodID, ...)根据methodID及传入方法变量执行方法
4<type> Call<type>MethodV(jobject obj, jmethodID methodID, va_list args)根据methodID及传入方法变量指针执行方法
5<type> Call<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args)根据methodID及传入方法变量数组执行方法
6<type> CallNonvirtual<type>Method(jobject obj, jmethodID methodID, ...)根据methodID及传入方法变量执行非虚实例方法
7<type> CallNonvirtual<type>MethodV(jobject obj, jmethodID methodID, va_list args)根据methodID及传入方法变量指针执行非虚实例方法
8<type> CallNonvirtual<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args)根据methodID及传入方法变量数组执行非虚实例方法
9<type> CallStatic<type>Method(jobject obj, jmethodID methodID, ...)根据methodID及传入方法变量执行静态方法
10<type> CallStatic<type>MethodV(jobject obj, jmethodID methodID, va_list args)根据methodID及传入方法变量指针执行静态方法
11<type> CallStatic<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args)根据methodID及传入方法变量数组执行静态方法
12<type> CallStaticNonvirtual<type>Method(jobject obj, jmethodID methodID, ...)根据methodID及传入方法变量执行静态非虚实例方法
13<type> CallStaticNonvirtual<type>MethodV(jobject obj, jmethodID methodID, va_list args)根据methodID及传入方法变量指针执行静态非虚实例方法
14<type> CallStaticNonvirtual<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args)根据methodID及传入方法变量数组执行静态非虚实例方法

关于异常的操作

在这里需要注意的是,jni层在出错的时候不会像java层一样,抛出异常,如果需要抛出java层的异常需要调用到如下的方法

序号方法名备注
1jint Throw(JNIEnv *env, jthrowable obj);obj:一个java.lang.Throwable对象。return:异常抛出结果。0表示异常正常抛出到JVM,否则异常抛出失败。
2jint ThrowNew(JNIEnv *env, jclass clazz,const char *message);抛出自定义异常clazz:待抛出的异常类。message:异常消息。要求UTF-8编码。return:异常抛出结果。0表示异常正常抛出到JVM,否则异常抛出失败。
3jthrowable ExceptionOccurred(JNIEnv *env);如果我们需要知道我们之前的操作是否存在JVM抛出的异常时,我们可以调用如下方法获取异常对象,注意:即使我们获取到了异常对象,这个异常仍然在JVM中存在,直到我们调用ExceptionClear方法清空异常。return:当前发生的异常对象。如果没有异常抛出则返回NULL。
4void ExceptionDescribe(JNIEnv *env);当我们拦截到一个异常,我们可以使用如下方法打印错误栈中的内容。就像Java中的printStackTrace:
5void ExceptionClear(JNIEnv *env);调用以下方法可以清空当前产生的全部异常信息
6void FatalError(JNIEnv *env, const char *msg);对于上述异常,我们可以产生也可以拦截。但是如果发生了一些错误导致我们的程序无法再正常运行下去了,则可以发送一个错误信息给JVM,此时程序将被终止。msg:错误信息。UTF-8。
7jboolean ExceptionCheck(JNIEnv *env);我们可以通过以下方法查看当前有没有异常产生。return:是否存在异常信息。

关于引用类型

在JNI中引用类型分为三种,分别是全局引用,局部引用和弱全局引用。
全局引用可以跨方法(本地方法返回后仍然有效),跨线程使用,直到手动释放才会失效。该引用不会被GC回收。相当于java中的强引用
局部引用是JVM负责的引用类型,其被JVM分配管理,并占用JVM的资源。局部引用在native方法返回后被自动回收。局部引用只在创建它们的线程中有效,不能跨线程传递。
弱全局引用是一种特殊的全局引用。跟普通的全局引用不同的是,一个弱全局引用允许Java对象被垃圾回收器回收。当垃圾回收器运行的时候,如果一个对象仅被弱全局引用所引用,则这个引用将会被回收。一个被回收了的弱引用指向NULL,开发者可以将其与NULL比较来判定该对象是否可用。

序号方法名备注
1jobject NewGlobalRef(JNIEnv *env, jobject obj);通过以下方法可以将任意引用转换为全局引用。由于全局引用不再受到JVM统一管理,所以我们要在不用时手动删除。obj:任意类型的引用。return:全局引用。如果内存不足返回NULL。
2void DeleteGlobalRef(JNIEnv *env, jobject globalRef);通过以下方法可以删除一个全局引用。globalRef:全局引用。
3jobject NewLocalRef(JNIEnv *env, jobject ref);通过以下方法可以创建一个局部引用。通常情况下,我们在native方法中创建的引用都是局部引用,并且不需要手动进行释放,当方法返回时,这个引用就会被自动销毁。ref:全局或者局部引用return:局部引用
4void DeleteLocalRef(JNIEnv *env, jobject localRef);通过以下方法可以删除一个局部引用。localRef:局部引用。
5jint EnsureLocalCapacity(JNIEnv *env, jint capacity);虚拟机将确保每个本地方法至少可以创建16个局部引用。但是在如今的场景中,16个局部引用已经远远不能满足开发需求了。为了为了解决这个问题,JNI提供了查询可用引用容量的方法,我们在创建超出限制的引用时最好先确认是否有足够的空间可以。capacity:给定局部引用的数量。return:JNI_OK表示当前线程栈可以创建capacity个局部引用。返回其他值表示不可用,并抛出一个OutOfMemoryError异常存在异常OutOfMemoryError
6jint PushLocalFrame(JNIEnv *env, jint capacity);局部栈帧的入栈capacity:给定局部引用的数量。return:JNI_OK表示当前线程栈可以创建capacity个局部引用。返回其他值表示不可用,并抛出一个OutOfMemoryError异常
7jobject PopLocalFrame(JNIEnv *env, jobject result);销毁其中的全部局部引用。result:给定保存栈帧的引用,如果不需要前一个栈帧则可以传入NULL。return:前一个帧的引用。
8jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);销毁其中的全部局部引用。result:给定保存栈帧的引用,如果不需要前一个栈帧则可以传入NULL。return:前一个帧的引用。
9jobject PopLocalFrame(JNIEnv *env, jobject result);新建弱引用obj:任意对象。return:返回弱全局引用,如果obj为NULL则返回NULL。
10void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);删除弱引用obj:弱全局引用。

关于字符串的一些操作

序号方法名备注
1jstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len);新建java字符串。unicodeChars:一个指向Unicode编码的字符数组的指针。len:unicodeChars的长度return:Java字符串对象
2jsize GetStringLength(JNIEnv *env, jstring string);获取jstring字符串的长度string:Java字符串对象return:字符串长度
3const jchar * GetStringChars(JNIEnv *env, jstring string,jboolean *isCopy);获取jstring字符串的字符串数组。isCopy:注意,这个参数很重要,这是一个指向Java布尔类型的指针。函数返回之后应当检查这个参数的值,如果值为JNI_TRUE表示返回的字符是Java字符串的拷贝,我们可以对其中的值进行任意修改。如果返回值为JNI_FALSE,表示这个字符指针指向原始Java字符串的内存,这时候对字符数组的任何修改都将会原始字符串的内容。如果你不关心字符数组的来源,或者说你的操作不会对字符数组进行任何修改,可以传入NULL。return:指向字节数组的指针
4void ReleaseStringChars(JNIEnv *env, jstring string,const jchar *chars);释放从Java字符串中获取的字符数组string:Java字符串对象。chars:字符数组。
5jstring NewStringUTF(JNIEnv *env, const char *bytes);新建UTF-8编码字符串。bytes:UTF-8编码的字节数组。return:UTF-8编码的Java字符串对象
6jsize GetStringUTFLength(JNIEnv *env, jstring string);获取UTF-8字符串的长度
7const char * GetStringUTFChars(JNIEnv *env, jstring string,jboolean *isCopy);获取UTF-8编码的Java字符串的
8void ReleaseStringUTFChars(JNIEnv *env, jstring string,const char *utf);释放从UTF-8字符串中获取的字符数组
9void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);从Java字符串中截取一段字符。str:Java字符串对象。start:起始位置。len:截取长度。buf:保存截取结果的缓冲区。
10void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);从UTF-8字符串中截取一段字符
11const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);直接获取字符串指针
12void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);直接获取字符串指针

关于java数组的一些操作

序号方法名备注
1jsize GetArrayLength(JNIEnv *env, jarray array);获取数组长度
2jobjectArray NewObjectArray(JNIEnv *env, jsize length,jclass elementClass, jobject initialElement);新建对象数组ength:数组的长度。elementClass:数组中的对象类型。initialElement:数组中的每个元素都会使用这个值进行初始化,可以为NULL。return:对象数组,创建失败返回NULL
3jobject GetObjectArrayElement(JNIEnv *env,jobjectArray array, jsize index);获取对象数组元素。array:对象数组,index:位置索引
4void SetObjectArrayElement(JNIEnv *env, jobjectArray array,jsize index, jobject value);设置对象数组元素
5j<type>Array New<type>Array(jsize length)新建对象数组元素
6<type>* Get<type>ArrayElements(jbooleanArray array, jboolean* isCopy)获取基本数据类型数组元素的函数原型
7void Release<type>ArrayElements(<type>Array array, jboolean* elems,jint mode)释放基本数据类型数组。mode 0 :copy back the content and free the elems buffer,mode JNI_COMMIT:copy back the content but do not free the elems buffer,mode JNI_ABORT:free the buffer without copying back the possible changes
8(Get<type>ArrayRegion)(JNIEnv, <type>Array,jsize, jsize, jboolean*);截取数组
9void Set<type>ArrayRegion(JNIEnv *env, ArrayType array,jsize start, jsize len, const NativeType *buf);设置数组范围
10void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);操作数组指针
11void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);操作数组指针

本地方法

当我们在一个Java文件中书写一个native的方法的时候,为了让JNI识别我们的方法,就需要采用注册的方式。

序号方法名备注
1jint RegisterNatives(JNIEnv *env, jclass clazz,const JNINativeMethod *methods, jint nMethods);注册本地方法。clazz: 包含本地方法的Java类。methods: 本地方法描述数组。nMethods: 数组长度。return:成功返回0,否则注册失败
2jint UnregisterNatives(JNIEnv *env, jclass clazz);return:返回0表示成功,否则为失败。解除本地方法,当我们确定不再需要本地方法的时候,可以调用这个方法来解除本地方法的注册。这个方法会导致本地库的重新加载和链接。



作者:月影路西法
链接:https://www.jianshu.com/p/4334f084b2a4
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dklkkdkdk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值