Overview
Today we continue to study the JNI array, this article is only as a note, in case you forget it in the future
Array
JNI divides java types into two types, basic data types and reference data types. Reference data types are uniformly represented by jobject, and arrays are also divided into basic data types and reference data types. Reference data types are represented by jobjectarray.
Array of basic data types
Let's first analyze the APIs related to arrays of basic data types
Get< PrimitiveType >ArrayElements
Returns an array of basic data types, which PrimitiveType
refers to the basic data types. For example, if you want to get an array of int data type, then it PrimitiveType
is int
NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, jboolean *isCopy);
- ArrayType array: represents the java array object
- jboolean *isCopy:
*isCopy
when JNI_TRUE, the pointer points to the copy of the original array,*isCop
when it is JNI_FALSE, the pointer points to the original array
If the function pointer points to the original array, all modifications are made on the original array. If the function pointer points to the copy array, then all modifications are made on the copy array, and the element group is not affected.
We, you need to ensure that when the copy occurs, the modified data can be synchronized to the original array, Release<PrimitiveType>ArrayElements
this is the role
Release< PrimitiveType >ArrayElements
void Release<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, NativeType *elems, jint mode);
- ArrayType array: the original array java object
- NativeType *elems: pointer of JNI array type, local array
- jint mode: Means
0
to synchronize the modification to the original array and release the local array
JNI_COMMIT
: Synchronize the modification to the original array, but do not release the local array
JNI_ABORT
: Do not synchronize the modification to the original array, but release the local array
When the Get<PrimitiveType>ArrayElements function does not copy, the mode has no effect
Actual combat
java code
public class TextJniArray {
static {
System.loadLibrary("textjniarray");
}
public native void textArray(int[] array);
}
C code
#include <jni.h>
#include <android/log.h>
static void native_text_array(JNIEnv *env, jobject jobject1, jintArray array) {
//获取数组长度
jsize length = env->GetArrayLength(array);
//获取本地数组
jint *native_intaray = env->GetIntArrayElements(array, NULL);
//操作本地数组
for(int i=0;i<length;i++){
native_intaray[i]+=100;
}
//释放本地数组
env->ReleaseIntArrayElements(array,native_intaray,0);
}
static const JNINativeMethod nativeMethod[] = {
{
"textArray", "([I)V", (void *) native_text_array}
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJniArray");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
transfer
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int[] a = {
0, 1, 2, 3, 4, 5};
for (int i = 0; i < 6; i++) {
Log.d("mmm调用前数组数据", a[i] + "/");
}
new TextJniArray().textArray(a);
for (int i = 0; i < 6; i++) {
Log.d("mmm调用后数组数据", a[i] + "/");
}
}
D/mmm调用前数组数据: 0/
D/mmm调用前数组数据: 1/
D/mmm调用前数组数据: 2/
D/mmm调用前数组数据: 3/
D/mmm调用前数组数据: 4/
D/mmm调用前数组数据: 5/
D/mmm调用后数组数据: 100/
D/mmm调用后数组数据: 101/
D/mmm调用后数组数据: 102/
D/mmm调用后数组数据: 103/
D/mmm调用后数组数据: 104/
D/mmm调用后数组数据: 105/
Get< PrimitiveType >ArrayRegion
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len, NativeType *buf);
- ArrayType array: java array object
- jsize start: copy start position
- jsize len: the length of the copy
- NativeType *buf: local buffer array
The function of this function is to copy the java array ArrayType array
to the local array NativeType *buf
. If you modify the local array and need to be synchronized to the java array, you need to callSet<PrimitiveType>ArrayRegion
Set< PrimitiveType >ArrayRegion
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len, const NativeType *buf);
- ArrayType array: java primitive array
- jsize start: start index
- jsize len: copy length
- const NativeType *buf: local buffer array
The function of this function is to NativeType *buf
write the local array data back to the original java array. The starting point is start and the length is len.
Actual combat
Only C code is posted this time
static void native_text_array1(JNIEnv *env, jobject jobject1, jintArray array) {
//获取数组长度
jsize length = env->GetArrayLength(array);
//创建本地缓存数组
jint native_array[length - 2];
//进行数组考别
env->GetIntArrayRegion(array, 2, length - 2, native_array);
for (int i = 0; i < length - 2; ++i) {
native_array[i] += 100;
}
//把修改数据写回原数组
env->SetIntArrayRegion(array, 2, length - 2, native_array);
}
D/mmm调用前数组数据: 0/
D/mmm调用前数组数据: 1/
D/mmm调用前数组数据: 2/
D/mmm调用前数组数据: 3/
D/mmm调用前数组数据: 4/
D/mmm调用前数组数据: 5/
D/mmm调用后数组数据: 0/
D/mmm调用后数组数据: 1/
D/mmm调用后数组数据: 102/
D/mmm调用后数组数据: 103/
D/mmm调用后数组数据: 104/
D/mmm调用后数组数据: 105/
New< PrimitiveType >Array
This method can create an array in the JNI layer, and then return to java
Actual combat
C code
static jintArray native_text_array2(JNIEnv *env, jobject jobject1) {
jintArray array = env->NewIntArray(2);
jint *native_array = env->GetIntArrayElements(array, NULL);
for (int i = 0; i < 2; ++i) {
native_array[i] = 100 + i;
}
env->ReleaseIntArrayElements(array, native_array, 0);
return array;
}
transfer
int[] a = new TextJniArray().textArray2();
for (int i = 0; i < 2; i++) {
Log.d("mmm数组数据", a[i] + "/");
}
D/mmm数组数据: 100/
D/mmm数组数据: 101/
Reference data type array
Let's take a look at the API that needs to be used
GetObjectArrayElement
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
Gets an array of array
index index
elements under
IsInstanceOf
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
Used to determine whether the object obj is an instance object of the class class
Actual combat
Ready to reference data types
public class Person {
public String name;
public Person(String name) {
this.name = name;
}
public void say() {
Log.d("mmm", name + "在说话");
}
}
Prepare native methods
public native void textArray3(Person[] persons);
Prepare for C implementation
static void native_text_array3(JNIEnv *env, jobject jobject1, jobjectArray jobjectArray1) {
//获取数组长度
jsize length = env->GetArrayLength(jobjectArray1);
//获取person的class对象
jclass jclass_person = env->FindClass("com.taobao.alinnkit.ndk1.Person");
if (jclass_person == NULL) {
return;
}
//获取方法
jmethodID jmethodId_say = env->GetMethodID(jclass_person, "say", "()V");
if (jmethodId_say == NULL) {
return;
}
for (int i = 0; i < length; ++i) {
//获取java引用数组中的元素
jobject jobject2 = env->GetObjectArrayElement(jobjectArray1, i);
//判断元素的类型
if (env->IsInstanceOf(jobject2, jclass_person)) {
//调用元素的方法
env->CallVoidMethod(jobject2, jmethodId_say);
}
}
}
transfer
Person[] people = new Person[2];
people[0] = new Person("jj");
people[1] = new Person("oj");
new TextJniArray().textArray3(people);
Print data
D/mmm: jj在说话
D/mmm: oj在说话
NewObjectArray
jobjectArray NewObjectArray(JNIEnv *env, jsize length,
jclass elementClass, jobject initialElement);
The function is NewObjectArray
to elementClass
create an length
array of reference data types in the JNI layer, the function will create a length of reference data type array according to the type
If the fourth parameter is specified, the array will be initialized. If NULL is specified, all elements of the array are null
SetObjectArrayElement
void SetObjectArrayElement(JNIEnv *env, jobjectArray array,
jsize index, jobject value);
Is an array, set the value under the index index to value
NewObject & NewObjectA & NewObjectV
jobject NewObject(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass clazz,
jmethodID methodID, const jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);
The difference between these three parameters is that the incoming parameters are not the same, but the effect is the same
- Parameters class representatives, java class object, you can
FindClass
be looking for - The parameter methodID refers to the java constructor, which can
GetMethodID
be obtained by using the parameter name passed in<init>
, and the return value of the parameter name is V
AllocObject
jobject AllocObject(JNIEnv *env, jclass clazz);
This function will allocate memory for objects of the clazz class, without calling the constructor of the class class, and return a reference to this object
Actual combat
Prepare the native method of java
public native Person[] textArray4(int length);
Prepare for C implementation
static jobjectArray native_text_array4(JNIEnv *env, jobject jobject1, jint length) {
jclass jclass_person = env->FindClass("com.taobao.alinnkit.ndk1.Person");
if (jclass_person == NULL) {
return NULL;
}
//获取person的构造方法
jmethodID jmethodId_gouzao = env->GetMethodID(jclass_person, "<init>", "(Ljava/lang/String;)V");
if (jmethodId_gouzao == NULL) {
return NULL;
}
//创建引用数组
jobjectArray array_person = env->NewObjectArray(length, jclass_person, NULL);
for (int i = 0; i < length; ++i) {
jobject person = NULL;
if (i == 0) {
//构造方法创建对象
person = env->NewObject(jclass_person, jmethodId_gouzao, env->NewStringUTF("小红"));
}
if (i == 1) {
person = env->AllocObject(jclass_person);
//直接分配内存
jmethodID setname = env->GetMethodID(jclass_person, "setName", "(Ljava/lang/String;)V");
env->CallVoidMethod(person, setname, env->NewStringUTF("小明"));
}
//把初始化好的对象赋值给数组
env->SetObjectArrayElement(array_person, i, person);
//释放局部变量
env->DeleteLocalRef(person);
}
return array_person;
}
transfer
Person[] people1 = new TextJniArray().textArray4(2);
for (int i = 0; i < people1.length; i++) {
people1[i].say();
}
Print data
D/mmm: 小红在说话
D/mmm: 小明在说话
reference
https://juejin.im/post/5d2ed0ac6fb9a07f0655a267