Android NDK开发(二)数组

在这篇文章里记录一下C语言操作java数组的相关方法,通过一个简单的程序实现:在c语言里使用java生成的数组,在java里使用c语言生成的数组。

数据类型对应关系

首先我们来看一下,c语言和java语言里数据类型是如何对应的

// 基本类型
typedef unsigned char   jboolean;       /* unsigned 8 bits */
typedef signed char     jbyte;          /* signed 8 bits */
typedef unsigned short  jchar;          /* unsigned 16 bits */
typedef short           jshort;         /* signed 16 bits */
typedef int             jint;           /* signed 32 bits */
typedef long long       jlong;          /* signed 64 bits */
typedef float           jfloat;         /* 32-bit IEEE 754 */
typedef double          jdouble;        /* 64-bit IEEE 754 */
typedef jint            jsize;
// 对象类型
typedef void*           jobject;
typedef jobject         jclass;
typedef jobject         jstring;
typedef jobject         jarray;
typedef jarray          jobjectArray;
typedef jarray          jbooleanArray;
typedef jarray          jbyteArray;
typedef jarray          jcharArray;
typedef jarray          jshortArray;
typedef jarray          jintArray;
typedef jarray          jlongArray;
typedef jarray          jfloatArray;
typedef jarray          jdoubleArray;
typedef jobject         jthrowable;
typedef jobject         jweak;

这里只截取与C语言有关的部分,如果想查看全部对应关系,请参考${ndk_dir}/platforms/android-14/arch-arm/usr/include/jni.h

相关方法

1、生成数组

    jobjectArray  (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
    // 参数第四个参数用来初始化数组(All elements are initially set to initialElement.)
    jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
    jbyteArray    (*NewByteArray)(JNIEnv*, jsize);
    jcharArray    (*NewCharArray)(JNIEnv*, jsize);
    jshortArray   (*NewShortArray)(JNIEnv*, jsize);
    jintArray     (*NewIntArray)(JNIEnv*, jsize);
    jlongArray    (*NewLongArray)(JNIEnv*, jsize);
    jfloatArray   (*NewFloatArray)(JNIEnv*, jsize);
    jdoubleArray  (*NewDoubleArray)(JNIEnv*, jsize);

2、 获取长度

jsize       (*GetArrayLength)(JNIEnv*, jarray);

3、获取数组里的内容

3、1、对象类型的数组

jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
// 最后一个参数是要获取的对象在数组里的下标

3、2、基本类型数组

    jboolean*   (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
    jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
    jchar*      (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
    jshort*     (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
    jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
    jlong*      (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
    jfloat*     (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
    jdouble*    (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);

A family of functions that returns the body of the primitive array. The result is valid until the corresponding ReleaseArrayElements() function is called. Since the returned array may be a copy of the Java array, changes made to the returned array will not necessarily be reflected in the original array until ReleaseArrayElements() is called.
If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy is made; or it is set to JNI_FALSE if no copy is made.
PS:第三个参数:JNI_TRUE或者JNI_FALSE或者0
PS:如果我们调用这些方法时没有把数组赋值一份,用完后也需要调用对应的ReleaseArrayElements方法释放资源,否则会内存泄漏。

4、给数组赋值

4、1、对象类型

void        (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);

4、2、基本数据类型

    void        (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
                        jboolean*, jint);
    void        (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
                        jbyte*, jint);
    void        (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
                        jchar*, jint);
    void        (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
                        jshort*, jint);
    void        (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
                        jint*, jint);
    void        (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
                        jlong*, jint);
    void        (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
                        jfloat*, jint);
    void        (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
                        jdouble*, jint);

最后一个参数:
0——-copy back the content and free the elems buffer
JNI_COMMIT——-copy back the content but do not free the elems buffer
JNI_ABORT——-free the buffer without copying back the possible changes

如果有其他参数不明白什么意思,可以参考http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp9502

sample

1、新建项目,添加native修饰的方法

public class MainActivity extends Activity {
    public native int[] testArray(byte[] array);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

2、在命令行,cd 到src目录下,执行命令

Android NDK开发(二)数组_第1张图片

Android NDK开发(二)数组_第2张图片

3、在项目跟目录下创建jni文件夹,把第二步生成的net_qingtian_array_MainActivity.h头文件拷贝在该目录下。

4、 在jni目录下新建一个c代码源文件,叫array.c,在array.c里把net_qingtian_array_MainActivity.h头文件里的方法给实现了

#include "net_qingtian_array_MainActivity.h"
#include <android/log.h>

JNIEXPORT jintArray JNICALL Java_net_qingtian_array_MainActivity_testArray
  (JNIEnv * env, jobject obj, jbyteArray _jbyteArray)
{
    // 获取数组长度
    jsize length = (*env)->GetArrayLength(env, _jbyteArray);
    __android_log_print(ANDROID_LOG_INFO, "qingtian", "byte数组长度:%d", length);

    // 获取c语言的数组
    jbyte* _bytes = (*env)->GetByteArrayElements(env, _jbyteArray, JNI_FALSE);

    // 打印数组
    int i = 0;
    for(i = 0 ; i < length ; i ++){
        __android_log_print(ANDROID_LOG_INFO, "qingtian", "byte数组第%d个值:%d", i, _bytes[i]);
    }

    // 释放byte数组资源
    (*env)->ReleaseByteArrayElements(env, _jbyteArray, _bytes, 0);

    // 生成jint数组,长度为传递进来的数组的两倍
    jsize newLength = length * 2;
    jintArray _jintArray = (*env)->NewIntArray(env, newLength);

    // 获取jintArray对应的本地数组
    jint* _jints = (*env)->GetIntArrayElements(env, _jintArray, JNI_FALSE);

    // 赋值
    for(i = 0 ; i < newLength ; i ++){
        _jints[i] = 20 + i;
    }

    // 释放int数组资源
    (*env)->ReleaseIntArrayElements(env, _jintArray, _jints, 0);

    // 返回int数组
    return _jintArray;
}

5、 在jni下编写Android.mk,这是和编译打包有关系的脚本文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
# 对应打包成函数库的名字
LOCAL_MODULE    := array
# 对应c代码的文件
LOCAL_SRC_FILES := array.c

LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

6、编译共享库。在linux环境下切换到项目根目录(cygwin),执行ndk-build命令。

这里写图片描述

7、在java方法里添加调用native方法的代码

public class MainActivity extends Activity {
    static {
        System.loadLibrary("array");
    }
    public native int[] testArray(byte[] array);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        byte[] bytes = new byte[]{0, 1, 2};
        int[] ints = testArray(bytes);
        Log.d("qingtian", "int数组的长度是:"+ints.length);
        for(int i = 0 ; i < ints.length ; i ++){
            Log.d("qingtian",  String.format("int数组第%d个值:%d", i, ints[i]));
        }
    }
}

8、运行项目。

Android NDK开发(二)数组_第3张图片

最后

祝大家元旦快乐。

你可能感兴趣的:(java,android,android,jni,C语言,NDK)