【JNI编程】JNI数组使用

我们经常遇到在native代码中处理数组的需求,JNI中数组的使用可以说是基本功。下面的例子演示了如何在JNI中获取数组的长度,JNI获取Java层的数组并使用等。

一、涉及API

1.1 GetArrayLength

jsize GetArrayLength(JNIEnv *env, jarray array);

返回数组中元素的数量。

LINKAGE:

JNIEnv接口函数表中的索引171。

PARAMETERS:

env:JNI接口指针。

array:一个Java数组对象。

RETURNS:

返回数组的长度。

1.2 NewArray

ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);

用于构造新原始数组对象的一系列操作。下表描述了特定的基本数组构造函数。您应该将NewArray替换为此表中的一个实际原始数组构造函数例程名称,并将ArrayType替换为该例程的相应数组类型。

NewArray 例程 数组类型
NewBooleanArray() jbooleanArray
NewByteArray() jbyteArray
NewCharArray() jcharArray
NewShortArray() jshortArray
NewIntArray() jintArray
NewLongArray() jlongArray
NewFloatArray() jfloatArray
NewDoubleArray() jdoubleArray

LINKAGE:

JNIEnv接口函数表中的索引。

NewArray例程 Index
NewBooleanArray() 175
NewByteArray() 176
NewCharArray() 177
NewShortArray() 178
NewIntArray() 179
NewLongArray() 180
NewFloatArray() 181
NewDoubleArray() 182

PARAMETERS:

env:JNI接口指针。

length:数组长度。

RETURNS:

返回Java数组,如果无法构造数组,则返回NULL。

1.3 GetArrayElements

NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy);

返回基本数组主体的一系列函数。结果有效,直到调用相应的ReleaseArrayElements()函数。由于返回的数组可能是Java数组的副本,因此在调用ReleaseArrayElements())之前,对返回数组所做的更改不一定会反映在原始数组中。

如果isCopy不为NULL,如果进行了复制,则*isCopy设置为JNI_TRUE; 如果没有复制,则设置为JNI_FALSE。

下表描述了特定的原始数组元素访问器。您应该进行以下替换:

  • 将GetArrayElements替换为下表中的一个实际原始元素访问器例程名称。
  • 将ArrayType替换为相应的数组类型。
  • 将NativeType替换为该例程的相应本地类型。

无论如何在JVM中表示布尔数组,GetBooleanArrayElements()始终返回指向jbooleans的指针,每个字节表示一个元素(解包表示)。其他类型的所有数组都保证在内存中是连续的。

GetArrayElements例程 数组类型 本地类型
GetBooleanArrayElements() jbooleanArray jboolean
GetByteArrayElements() jbyteArray jbyte
GetCharArrayElements() jcharArray jchar
GetShortArrayElements() jshortArray jshort
GetIntArrayElements() jintArray jint
GetLongArrayElements() jlongArray jlong
GetFloatArrayElements() jfloatArray jfloat
GetDoubleArrayElements() jdoubleArray jdouble

LINKAGE:

JNIEnv接口函数表中的索引。

GetArrayElements例程 Index
GetBooleanArrayElements() 183
GetByteArrayElements() 184
GetCharArrayElements() 185
GetShortArrayElements() 186
GetIntArrayElements() 187
GetLongArrayElements() 188
GetFloatArrayElements() 189
GetDoubleArrayElements() 190

PARAMETERS:

env:JNI接口指针。

array:一个Java字符串对象。

isCopy:指向布尔值的指针。

RETURNS:

返回指向数组元素的指针,如果操作失败则返回NULL。

1.4 ReleaseArrayElements

void Release<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode);

一系列函数,通知VM本地代码不再需要访问elems。elems参数是使用相应的GetArrayElements()函数从数组派生的指针。如有必要,此函数会将对elems所做的所有更改复制回原始数组。

mode参数提供有关如何释放数组缓冲区的信息。如果elems不是数组中元素的副本,则mode无效。否则,模式会产生以下影响,如下表所示:

模式 行为
0 复制回内容并释放elems缓冲区
JNI_COMMIT 复制回内容,但不释放elems缓冲区
JNI_ABORT 释放缓冲区而不复制回可能的更改

在大多数情况下,程序员将“0”传递给mode参数,以确保固定和复制数组的一致行为。其他选项使程序员可以更好地控制内存管理,并且应该非常谨慎地使用。

下表描述了构成原始数组处理者系列的特定例程。您应该进行以下替换:

  • 将ReleaseArrayElements替换为下表中的一个实际原始数组处理程序例程名称。
  • 将ArrayType替换为相应的数组类型。
  • 将NativeType替换为该例程的相应本地类型。
ReleaseArrayElements 数组类型 本地类型
ReleaseBooleanArrayElements() jbooleanArray jboolean
ReleaseByteArrayElements() jbyteArray jbyte
ReleaseCharArrayElements() jcharArray jchar
ReleaseShortArrayElements() jshortArray jshort
ReleaseIntArrayElements() jintArray jint
ReleaseLongArrayElements() jlongArray jlong
ReleaseFloatArrayElements() jfloatArray jfloat
ReleaseDoubleArrayElements() jdoubleArray jdouble

LINKAGE:

JNIEnv接口函数表中的索引。

ReleaseArrayElements 索引
ReleaseBooleanArrayElements() 191
ReleaseByteArrayElements() 192
ReleaseCharArrayElements() 193
ReleaseShortArrayElements() 194
ReleaseIntArrayElements() 195
ReleaseLongArrayElements() 196
ReleaseFloatArrayElements() 197
ReleaseDoubleArrayElements() 198

PARAMETERS:

env:JNI接口指针。

array:一个Java数组对象。

elems:指向数组元素的指针。

mode:释放模式。

1.5 GetArrayRegion

void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);

一组函数,将原始数组的某个区域复制到缓冲区中。

下表描述了特定的原始数组元素访问器,你应该做以下替换:

  • 将GetArrayRegion替换为下表中的一个实际原始元素访问器例程名称。
  • 将ArrayType替换为相应的数组类型。
  • 将NativeType替换为该例程的相应本地类型。
GetArrayRegion 数组类型 本地类型
GetBooleanArrayRegion() jbooleanArray jboolean
GetByteArrayRegion() jbyteArray jbyte
GetCharArrayRegion() jcharArray jchar
GetShortArrayRegion() jshortArray jshort
GetIntArrayRegion() jintArray jint
GetLongArrayRegion() jlongArray jlong
GetFloatArrayRegion() jfloatArray jloat
GetDoubleArrayRegion() jdoubleArray jdouble

LINKAGE:

JNIEnv接口函数表中的索引。

GetArrayRegion 索引
GetBooleanArrayRegion() 199
GetByteArrayRegion() 200
GetCharArrayRegion() 201
GetShortArrayRegion() 202
GetIntArrayRegion() 203
GetLongArrayRegion() 204
GetFloatArrayRegion() 205
GetDoubleArrayRegion() 206

PARAMETERS:

env:JNI接口指针。

array:一个Java数组。

start:起始索引。

len:要复制的元素数。

buf:目标缓冲区。

THROWS:

ArrayIndexOutOfBoundsException: 如果区域中的某个索引无效。

1.6 SetArrayRegion

void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, const NativeType *buf);

一组函数,用于从缓冲区中复制回原始数组的某个区域。

下表描述了特定的原始数组元素访问器。您应该进行以下替换:

将SetArrayRegion替换为下表中的一个实际原始元素访问器例程名称。
将ArrayType替换为相应的数组类型。
将NativeType替换为该例程的相应本地类型。

SetArrayRegion 数组类型 本地类型
SetBooleanArrayRegion() jbooleanArray jboolean
SetByteArrayRegion() jbyteArray jbyte
SetCharArrayRegion() jcharArray jchar
SetShortArrayRegion() jshortArray jshort
SetIntArrayRegion() jintArray jint
SetLongArrayRegion() jlongArray jlong
SetFloatArrayRegion() jfloatArray jfloat
SetDoubleArrayRegion() jdoubleArray jdouble

LINKAGE:

JNIEnv接口函数表中的索引。

SetArrayRegion 索引
SetBooleanArrayRegion() 207
SetByteArrayRegion() 208
SetCharArrayRegion() 209
SetShortArrayRegion() 210
SetIntArrayRegion() 211
SetLongArrayRegion() 212
SetFloatArrayRegion() 213
SetDoubleArrayRegion() 214

PARAMETERS:

env:JNI接口指针。

array:一个Java数组。

start:起始索引。

len:要复制的元素数。

buf:源缓冲区。

THROWS:

ArrayIndexOutOfBoundsException: 如果区域中的某个索引无效。

注意

从JDK/JRE 1.1开始,程序员可以使用Get/ReleaseArrayElements函数来获取指向原始数组元素的指针。如果VM支持固定,则返回指向原始数据的指针; 否则,制作副本。从JDK/JRE 1.3开始引入的新功能允许本地代码获取指向数组元素的直接指针,即使VM不支持固定。

二、实际使用

本实例通过在native代码中修改传入的数组,观察不同的JNI函数的作用。我们不难发现GetIntArrayElements和ReleaseIntArrayElements要成对使用,如果乱用,会出现意想不到的问题。同时在ReleaseIntArrayElements函数中试验了参数JNI_ABORT,发现native修改并未生效。最后验证了GetIntArrayRegion、SetIntArrayRegion和NewIntArray。这些函数都是我们在JNI操作数组中经常遇到的。

package ndk.example.com.ndkexample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main";

    static {
        System.loadLibrary("native-lib");
    }

    private int[] mTestIntArr = new int[]{0, 1, 2, 3, 4, 5};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = findViewById(R.id.sample_text);
        tv.setText("数组使用...");

        Log.d(TAG, "before nativeModifyIntArr test[0]=" + mTestIntArr[0]);
        nativeModifyIntArr(mTestIntArr);
        Log.d(TAG, "after nativeModifyIntArr test[0]=" + mTestIntArr[0]);

        Log.d(TAG, "before nativeReadIntArr test[0]=" + mTestIntArr[0]);
        nativeReadIntArr(mTestIntArr);
        Log.d(TAG, "after nativeReadIntArr test[0]=" + mTestIntArr[0]);

        Log.d(TAG, "before nativeModifyIntArr1 test[0]=" + mTestIntArr[0] + ",test[1]=" + mTestIntArr[1]);
        nativeModifyIntArr1(mTestIntArr);
        Log.d(TAG, "after nativeModifyIntArr1 test[0]=" + mTestIntArr[0] + ",test[1]=" + mTestIntArr[1]);

        Log.d(TAG, "before nativeReadIntArr1 test[0]=" + mTestIntArr[0] + ",test[1]=" + mTestIntArr[1]);
        nativeReadIntArr1(mTestIntArr);
        Log.d(TAG, "after nativeReadIntArr1 test[0]=" + mTestIntArr[0] + ",test[1]=" + mTestIntArr[1]);

        Log.d(TAG, "before nativeModifyIntArr2 test[0]=" + mTestIntArr[0] + ",test[1]=" + mTestIntArr[1]);
        nativeModifyIntArr2(mTestIntArr);
        Log.d(TAG, "after nativeModifyIntArr2 test[0]=" + mTestIntArr[0] + ",test[1]=" + mTestIntArr[1]);

        mTestIntArr = nativeArr();
        for (int ele : mTestIntArr) {
            Log.d(TAG, "after nativeArr ele=" + ele);
        }

    }

    public native void nativeModifyIntArr(int[] arr);

    public native void nativeModifyIntArr1(int[] arr);

    public native void nativeModifyIntArr2(int[] arr);

    public native void nativeReadIntArr1(int[] arr);

    public native void nativeReadIntArr(int[] arr);

    public native int[] nativeArr();

}

Native代码:

#include <jni.h>
#include <string.h>
#include <android/log.h>

#define TAG "Native"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))

extern "C" {

JNIEXPORT void JNICALL
Java_ndk_example_com_ndkexample_MainActivity_nativeModifyIntArr(JNIEnv *env, jobject instance, jintArray arr_) {
    jint *arr = env->GetIntArrayElements(arr_, NULL);
    if (NULL != arr) {
        arr[0] = 1000;
        env->ReleaseIntArrayElements(arr_, arr, 0);
    }
}

JNIEXPORT void JNICALL
Java_ndk_example_com_ndkexample_MainActivity_nativeReadIntArr(JNIEnv *env, jobject instance, jintArray arr_) {
    //此代码会报错,无法设置* isCopy = JNI_TRUE
    //jint *arr = env->GetIntArrayElements(arr_, reinterpret_cast<jboolean *>(JNI_TRUE));
    jint *arr = env->GetIntArrayElements(arr_, NULL);
    if (NULL != arr) {
        arr[0] = -1000;
        env->ReleaseIntArrayElements(arr_, arr, JNI_ABORT);
    }
}

JNIEXPORT void JNICALL
Java_ndk_example_com_ndkexample_MainActivity_nativeModifyIntArr1(JNIEnv *env, jobject instance, jintArray arr_) {
    jint *arr = env->GetIntArrayElements(arr_, NULL);
    if (NULL != arr) {
        arr[0] = -1000;
        env->ReleaseIntArrayElements(arr_, arr, JNI_COMMIT);
        arr[1] = -1000;
        env->ReleaseIntArrayElements(arr_, arr, 0);
    }
}

JNIEXPORT void JNICALL
Java_ndk_example_com_ndkexample_MainActivity_nativeReadIntArr1(JNIEnv *env, jobject instance, jintArray arr_) {
    jint len = env->GetArrayLength(arr_);
    LOGI("array len=%d", len);
    jint *buf = new jint[len];
    for (int i = 0; i < len; i++) {
        buf[i] = 0;
    }
    env->GetIntArrayRegion(arr_, 0, len, buf);
    for (int i = 0; i < len; i++) {
        LOGI("buf[%d]=%d", i, buf[i]);
    }
}

JNIEXPORT void JNICALL
Java_ndk_example_com_ndkexample_MainActivity_nativeModifyIntArr2(JNIEnv *env, jobject instance, jintArray arr_) {
    jint len = env->GetArrayLength(arr_);
    jint *buf = new jint[len];
    for (int i = 0; i < len; i++) {
        buf[i] = 0;
    }
    env->SetIntArrayRegion(arr_, 0, len, buf);
}

JNIEXPORT jintArray JNICALL
Java_ndk_example_com_ndkexample_MainActivity_nativeArr(JNIEnv *env, jobject instance) {
    int len = 3;
    jintArray intArr = env->NewIntArray(len);
    jint *arr = env->GetIntArrayElements(intArr, NULL);
    if (NULL != arr) {
        for (int i = 0; i < len; i++) {
            arr[i] = 99;
        }
        env->ReleaseIntArrayElements(intArr, arr, 0);
    }

    return intArr;
}

}


运行结果

10-16 11:14:53.319 28150-28150/? D/Main: before nativeModifyIntArr test[0]=0
    after nativeModifyIntArr test[0]=1000
    before nativeReadIntArr test[0]=1000
    after nativeReadIntArr test[0]=1000
    before nativeModifyIntArr1 test[0]=1000,test[1]=1
    after nativeModifyIntArr1 test[0]=-1000,test[1]=-1000
    before nativeReadIntArr1 test[0]=-1000,test[1]=-1000
10-16 11:14:53.320 28150-28150/? I/Native: array len=6
    buf[0]=-1000
    buf[1]=-1000
    buf[2]=2
    buf[3]=3
    buf[4]=4
    buf[5]=5
10-16 11:14:53.320 28150-28150/? D/Main: after nativeReadIntArr1 test[0]=-1000,test[1]=-1000
    before nativeModifyIntArr2 test[0]=-1000,test[1]=-1000
    after nativeModifyIntArr2 test[0]=0,test[1]=0
    after nativeArr ele=99
    after nativeArr ele=99
    after nativeArr ele=99
发布了64 篇原创文章 · 获赞 42 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/tyyj90/article/details/87640273
JNI