NDK/JNI Develop Guide (4) How to use Array


NDK/JNI Develop Guide (4) How to use Array
JNIの配列は基本型配列とオブジェクト配列に分けられており、それらの処理方法は異なり、基本型配列のすべての要素はJNIの基本データ型であり、直接アクセスすることができる.オブジェクト配列のすべての要素はクラスのインスタンスまたは他の配列の参照であり、文字列操作と同様にJavaがJNI層に渡す配列に直接アクセスすることはできません.Java層の配列オブジェクトにアクセスおよび設定するには、適切なJNI関数を選択する必要があります.JNIとJavaデータ型のマッピング関係を理解しているとします.次にintタイプを例に、基本データ型配列へのアクセス方法を説明します.オブジェクト配列タイプは、2 D配列を作成する例でアクセス方法を示します.

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("math");// load library to android project
    }

    private int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    private TextView sumTxt;
    private TextView printArr;

    @SuppressLint("DefaultLocale")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sumTxt = (TextView) findViewById(R.id.sun_txt);
        printArr = (TextView) findViewById(R.id.print_arr);
        IntArray intArray = new IntArray();
        int sum = intArray.sumArray(array);
        Toast.makeText(this, "The Attays sum is " + sum, Toast.LENGTH_SHORT).show();
        sumTxt.setText("The Attays sum is " + sum);
        int[][] arr = intArray.initInt2DArray(3);
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 3; i++) {//get the arrays and add those into builder
            for (int j = 0; j < 3; j++) {
                stringBuilder.append(String.format("arr[%d][%d] = %d
"
,i,j,arr[i][j])); System.out.format("arr[%d][%d] = %d
"
, i, j, arr[i][j]); } } printArr.setText(stringBuilder.toString());//show on the TextView } }

次の2つのローカルメソッドを開きます.
/** * Created by blueZhang on 16/6/28. * * @Author: BlueZhang * @date: 16/6/28 */
public class IntArray {
    public native int sumArray(int[] arr);//caculate the sum of array from java 
    public native int[][] initInt2DArray(int size);//init the array and return the result
}

ローカルコードの実装を見てみましょう.
//
// Created by blueZhang on 16/6/28.
//
#include "sum.h"
#include "stdlib.h"
#include "string.h"

/* * Class: com_example_bluezhang_ndkarraytest_IntArray * Method: sumArray * Signature: ([I)I */
JNIEXPORT jint JNICALL Java_com_example_bluezhang_ndkarraytest_IntArray_sumArray
        (JNIEnv *env, jobject obj, jintArray j_array) {
    jint i, sum = 0;
    jint *c_array;
    jint arr_len;
    //1. get the length of the array
    arr_len = (*env)->GetArrayLength(env, j_array);
    //2.get the buffer(java array element) from memory depend on data type
    c_array = (jint *) malloc(sizeof(jint) * arr_len);
    //3. Clear the cache area
    memset(c_array      , 0, sizeof(jint) * arr_len);
    //4. Copy all elements in the Java array to the buffer
    (*env)->GetIntArrayRegion(env, j_array, 0, arr_len, c_array);
    for (i = 0; i < arr_len; i++) {
        sum += c_array[i];  //5. sum all elements
    }
    free(c_array);  //6. release the buffer
    return sum;
// jint i, sum = 0;
// jint *c_array;
// jint arr_len;
// // May be discontinuous in memory of the elements in an array, the JVM may copy all the raw data to the buffer, and then return a pointer to the buffer
// c_array = (*env)->GetIntArrayElements(env,j_array,NULL);
// if (c_array == NULL) {
// return 0; // copy into the buffer faliure
// }
// arr_len = (*env)->GetArrayLength(env,j_array);
// printf("arr_len = %d
", arr_len);
// for (i = 0; i < arr_len; i++) { // sum += c_array[i]; // } // (*env)->ReleaseIntArrayElements(env,j_array, c_array, 0); //release the buffer // return sum; } /* * Class: com_example_bluezhang_ndkarraytest_IntArray * Method: initInt2DArray * Signature: (I)[[I */ JNIEXPORT jobjectArray JNICALL Java_com_example_bluezhang_ndkarraytest_IntArray_initInt2DArray (JNIEnv *env, jobject obj, jint size) { jobjectArray result; jclass clsIntArray; jint i,j; // 1.Get an int type 2 d array class reference clsIntArray = (*env)->FindClass(env,"[I"); if (clsIntArray == NULL) { return NULL; } // 2.Create an array object (the inside clsIntArray in each element) result = (*env)->NewObjectArray(env,size,clsIntArray,NULL); if (result == NULL) { return NULL; } // 3.assignment for array element for (i = 0; i < size; ++i) { jint buff[256]; jintArray intArr = (*env)->NewIntArray(env,size); if (intArr == NULL) { return NULL; } for (j = 0; j < size; j++) { buff[j] = i + j; } (*env)->SetIntArrayRegion(env,intArr, 0,size,buff); (*env)->SetObjectArrayElement(env,result, i, intArr); (*env)->DeleteLocalRef(env,intArr); } return result; }

ここではandroid studio生成の使い方をもう一度説明します.hファイル:
          :
/Users/bluezhang/Desktop/NDKArrayTest/app/build/intermediates/classes/debug
    :javah -jni packagename.classname
       .h

前の例では、JavaでsumArrayのnativeメソッドが定義されています.パラメータタイプはint[]で、JNIのjintArrayタイプに対応しています.ローカルコードでは、まずJNIのGetArrayLength関数によって配列の長さを取得し、既知の配列はjintArrayタイプであり、配列の要素タイプがjintであることを導出し、配列の長さと配列要素タイプに基づいて対応するサイズのバッファを申請する.バッファが大きくなければ、もちろんスタックでメモリを直接申請することもできますが、効率は高くなりますが、Java配列のサイズが変わったので、ローカルコードも変更されます.次にGetIntArrayRegion関数を呼び出してJava配列のすべての要素をCバッファにコピーし、配列のすべての要素の和を加算し、java配列の要素を格納するCバッファを最後に解放し、計算結果を返します.GetIntArrayRegion関数の1番目のパラメータはJNIEnv関数ポインタ、2番目のパラメータはJava配列オブジェクト、3番目のパラメータはコピー配列の開始インデックス、4番目のパラメータはコピー配列の長さ、5番目のパラメータはコピー先です.ここで私たちの第一歩の仕事の境地は完成して、今私たちがしなければならないのは自分のプロジェクトのbuildを配置することです.gradleファイル.生成soファイルは作成後に呼び出すことができます.soファイルはarrayの和を計算し、arrayを初期化しjavaコードにフィードバックすることで、生成したarrayを使用して展示することができます.上記のコードでは、データの計算を行うために1つの方法を使用します.次のコメントのコードは、別の実装方法です.興味のある人は自分で手動で実現して、関連資料を調べることができます.
小結
1、     、       ,    Get/SetArrayRegion               。    
          C            ,      Stack( )   malloc        
,           。        ,                            
        ?       ,                  ,      。       
      ,                             (SetArrayRegion     
   ),              ,       ,        ArrayIndexOutOfBoundsException  。
2、        C   ,            ,                       
,  Get/ReleasePrimitiveArrayCritical   ,  Get/ReleaseStringCritical     , 
          ,    。
3、Get/Release<type>ArrayElements          ,JVM           ,      
      ,            。

オブジェクト配列へのアクセス
生成されたばかりのコードには2つのJNIの言い訳があることがわかります.
/* * Class: com_example_bluezhang_ndkarraytest_IntArray * Method: sumArray * Signature: ([I)I */
JNIEXPORT jint JNICALL Java_com_example_bluezhang_ndkarraytest_IntArray_sumArray
  (JNIEnv *, jobject, jintArray);

/* * Class: com_example_bluezhang_ndkarraytest_IntArray * Method: initInt2DArray * Signature: (I)[[I */
JNIEXPORT jobjectArray JNICALL Java_com_example_bluezhang_ndkarraytest_IntArray_initInt2DArray
  (JNIEnv *, jobject, jint);

生成される2つ目の方法は、Java内のオブジェクト配列にアクセスして初期化した配列が3*3の2次元配列であり、配列要素の値が要素コーナースケールの和であり、この配列を返すことです.ローカル関数initInt 2 DArrayはまずJNI関数FindClassを呼び出してint型の2次元配列クラスの参照を得る.FindClassに渡されるパラメータ"[I]は、JVMのint[]タイプに対応するJNIクラス記述子(JNIタイプ記述子、後述)です.int[]クラスのロードに失敗すると、FindClassはNULLを返し、java.lang.NoClassDefFoundError:[I異常です.次に、NewObjectArrayは新しい配列を作成します.この配列の要素タイプはintArrCls(int[])タイプで表されます.関数NewObjectArrayには1次元のみが割り当てられ、JVMには多次元配列に対応するデータ構造はなく、JNIにも似たような関数が2次元配列を作成することはありません.JNIの2次元配列はJVMのデータ構造を直接操作しているため、JAVAやC/C++の2次元配列の作成よりも複雑です.2 D配列にデータを設定する方法も非常に直接的で、まずNewIntArrayでJNIのint配列を作成し、各配列要素に空間を割り当て、次いでbuff[バッファ]の内容をSetIntArrayRegionで新しく割り当てられた1 D配列にコピーし、最後に外層サイクルでint[配列をjobjectArray配列に順次割り当て、1次元配列に1次元配列を組み込むと,いわゆる2次元配列が形成される.また、ループ内に多数のJNIローカルリファレンスが作成され、JNIリファレンステーブルがオーバーフローしないようにするため、外部ループでは毎回DeleteLocalRefを呼び出して新しく作成したjintArrayリファレンスをリファレンステーブルから削除します.JNIでは、jobjectおよびサブクラスのみが参照変数に属し、参照テーブルのスペースを占有します.jint、jfloat、jbooleanなどは基本タイプの変数で、参照テーブルのスペースを占有しません.つまり、解放する必要はありません.参照テーブルの最大スペースは512個で、この範囲を超えるとJVMは削除されます.