目录

  • 1. 使用jstring
  • 2. 说一下 GetStringUTFChars 和 ReleaseStringUTFChars
  • 3. 基本类型数组的例子
  • 4. 访问原始对象的变量
  • 5. 访问对象的static 变量
  • 6. 回调实例方法和静态方法
  • 7. 调用对象父类的方法
  • 8. 在Native中调用对象构造器
  • 9. 数组对象的创建


1. 使用jstring

JNI中类型是jstring,要正确使用需要转换为native的char*

  1. jstring转换为char*

    通过env调用const char* GetStringUTFChars(JNIEnv, jstring, jboolean)

  2. char* 转换为 jstring

    通过env调用  jstring NewStringUTF(JNIEnv, char)

例子:

实现传入string 返回一个新的string

native函数:

native public String getString(String name);

JNI代码

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

jstring getString(JNIEnv * env, jobject thisObj, jstring name) {
    const char* c_name = env->GetStringUTFChars(name, JNI_FALSE);
    std::cout << c_name << std::endl;
    env->ReleaseStringUTFChars(name, c_name);  // release resources
    return env->NewStringUTF(std::string("im here").c_str());
}
static JNINativeMethod methods[] = {
    {"getString", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getString},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

2. 说一下 GetStringUTFChars 和 ReleaseStringUTFChars

函数原型

// UTF-8 String (encoded to 1-3 byte, backward compatible with 7-bit ASCII)
// Can be mapped to null-terminated char-array C-string
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
   // Returns a pointer to an array of bytes representing the string in modified UTF-8 encoding.
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
   // Informs the VM that the native code no longer needs access to utf.
jstring NewStringUTF(JNIEnv *env, const char *bytes);
   // Constructs a new java.lang.String object from an array of characters in modified UTF-8 encoding.
jsize GetStringUTFLength(JNIEnv *env, jstring string);
   // Returns the length in bytes of the modified UTF-8 representation of a string.
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize length, char *buf);
   // Translates len number of Unicode characters beginning at offset start into modified UTF-8 encoding 
   // and place the result in the given buffer buf.
  
// Unicode Strings (16-bit character)
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
   // Returns a pointer to the array of Unicode characters
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
   // Informs the VM that the native code no longer needs access to chars.
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize length);
   // Constructs a new java.lang.String object from an array of Unicode characters.
jsize GetStringLength(JNIEnv *env, jstring string);
   // Returns the length (the count of Unicode characters) of a Java string.
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize length, jchar *buf);
   // Copies len number of Unicode characters beginning at offset start to the given buffer buf

GetStringUTFChars :

函数被作用转换jstring to  char*,当返回值是NULL,代表内存不足所以应该每次都check返回值是否是NULL

第三个参数 被设置为JNI_TRUE代表 返回值的是一个jstring的拷贝;

当是JNI_FALSE 返回的是直接指向String的指针,并且native代码无法修改这个指针的值

一般都设置为JNI_FALSE

ReleaseStringUTFChars:

当你不需要上面得到的指针,使用这个函数来释放内存

一般这两个就是配对使用

3. 基本类型数组的例子

属于引用类型,所以需要进行转换才能使用

between jintArray and C's jint[], or jdoubleArray and C's jdouble[]

两种方向的转换函数:

  1. jintArray转换为 jint[]

    env调用jint* GetIntArrayElements(JNIEnv *env, jintArray a, jboolean *iscopy)

    第三个参数和之前的含义相同

    1. jint[] 转换为 jintArray

    2. env调用 jintArray NewIntArray(JNIEnv *env, jsize len) 来分配对应大小的内存

    3. 然后调用void SetIntArrayRegion(JNIEnv *env, jintArray a, jsize start, jsize len, const jint *buf)

      将buf的内容 从 start 拷贝len 到 jintArray中

例子:

实现传入 int[] 数组返回值 是double数组

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

jdoubleArray getAverge(JNIEnv * env, jobject thisObj, jintArray valueList) {
    jint* array = env->GetIntArrayElements(valueList, JNI_FALSE);
    if ( array == NULL ) return NULL;
    jsize length = env->GetArrayLength(valueList);

    jint sum = 0;
    int i;
    for(int i=0; i < length; i++) {
        sum += array[i];
    }
    array[0] = 16;
    env->ReleaseIntArrayElements(valueList, array, JNI_FALSE); // release resources
    jdouble average = (jdouble) sum / length;
    jdouble outArray[] = {sum, average};
    jdoubleArray outJNIarray = env->NewDoubleArray(2);
    if (outArray == NULL) return NULL;
    env->SetDoubleArrayRegion(outJNIarray, 0, 2, outArray);
    return outJNIarray;
}

static JNINativeMethod methods[] = {
    {"getAverge", "([I)[D", (void *)getAverge},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

传入的是数组,可以直接修改数组内容

上面针对基本类型数组的常用参数如下

注意的是Release 的功能也是释放但是后面多了一个mode,这个mode 为0代表对数组进行的操作会同步到原始数组,目前只用过JNI_FALSE

NativeType * Get<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy);
void Release<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode);
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize length, NativeType *buffer);
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize length, const NativeType *buffer);
ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);

4. 访问原始对象的变量

java侧的程序

public class Main {
    static {
        System.loadLibrary("hello");
    }
    private int number = 88;
    private String message = "Hello java";
    public static void main(String[] args) {
        Main test = new Main();
        System.out.println("before change " + test.number + " " + test.message);
        test.modifyInstanceVariable();
        System.out.println("end change " + test.number + " " + test.message);
    }
    native public void modifyInstanceVariable();
}

native程序

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

void modifyInstanceVariable(JNIEnv * env, jobject thisObj) {
       // Get a reference to this object's class
    jclass thisClass = env->GetObjectClass(thisObj);

    jfieldID fidNumber = env->GetFieldID(thisClass, "number", "I");
    if (NULL == fidNumber) return;

    jint number = env->GetIntField(thisObj, fidNumber);
    printf("In C, the int is %d\n", number);
    number = 99;
    env->SetIntField(thisObj, fidNumber, number);

    jfieldID fidMessage = env->GetFieldID(thisClass, "message", "Ljava/lang/String;");
    if (NULL == fidMessage) return;
    jstring message = (jstring)env->GetObjectField(thisObj, fidMessage);

    const char *cStr = env->GetStringUTFChars(message, NULL);
    if (NULL == cStr) return;
    printf("In C, the string is %s\n", cStr);

    env->ReleaseStringUTFChars(message, cStr);
    message = env->NewStringUTF("Hello from C");
    if (NULL == message) return;

    env->SetObjectField(thisObj, fidMessage, message);
}

static JNINativeMethod methods[] = {
    {"modifyInstanceVariable", "()V", (void *)modifyInstanceVariable},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

核心步骤就是

  1. 获得对象类的引用

  2. 通过类引用得到访问此实例对象的ID, GetFieldID()

    需要提供这个变量的签名

  3. 通过Get<primitive-type>Field得到基于 实例对象ID的变量

  4. 更新变量使用Set<primitive-type>Field(),需要实例ID

基本函数原型就是
jclass GetObjectClass(JNIEnv *env, jobject obj);
   // Returns the class of an object.
   
jfieldID GetFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig);
  // Returns the field ID for an instance variable of a class.
 
NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);
  // Get/Set the value of an instance variable of an object
  // <type> includes each of the eight primitive types plus Object

5. 访问对象的static 变量

java函数

public class Main {
    static {
        System.loadLibrary("hello");
    }
    private static double number = 55.66;
    public static void main(String[] args) {
        Main test = new Main();
        System.out.println("before change " + test.number);
        test.modifyInstanceVariable();
        System.out.println("end change " + test.number);
    }
    native public void modifyInstanceVariable();
}

native函数

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

void modifyInstanceVariable(JNIEnv * env, jobject thisObj) {
       // Get a reference to this object's class
   // Get a reference to this object's class
   jclass cls = env->GetObjectClass(thisObj);

   // Read the int static variable and modify its value
   jfieldID fidNumber = env->GetStaticFieldID(cls, "number", "D");
   if (NULL == fidNumber) return;
   jdouble number = env->GetStaticDoubleField(cls, fidNumber);
   printf("In C, the double is %f\n", number);
   number = 77.88;
   env->SetStaticDoubleField(cls, fidNumber, number);
}

static JNINativeMethod methods[] = {
    {"modifyInstanceVariable", "()V", (void *)modifyInstanceVariable},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

核心就是换了一套函数

jfieldID GetStaticFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig);
  // Returns the field ID for a static variable of a class.
 
NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);
void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);
  // Get/Set the value of a static variable of a class.
  // <type> includes each of the eight primitive types plus Object.

6. 回调实例方法和静态方法

java函数

public class Main {
    static {
        System.loadLibrary("hello");
    }
    // Declare a native method that calls back the Java methods below

    private native void nativeMethod();

    // To be called back by the native code
    private void callback() {
        System.out.println("In Java");
    }

    private void callback(String message) {
        System.out.println("In Java with " + message);
    }

    private double callbackAverage(int n1, int n2) {
        return ((double)n1 + n2) / 2.0;
    }

    // Static method to be called back
    private static String callbackStatic() {
        return "From static Java method";
    }

    public static void main(String[] args) {
        Main test = new Main();
        test.nativeMethod();
    }
}

native函数

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

void nativeMethod(JNIEnv * env, jobject thisObj) {
     // Get a class reference for this object
       jclass thisClass = env->GetObjectClass(thisObj);

       // Get the Method ID for method "callback", which takes no arg and return void
       jmethodID midCallBack = env->GetMethodID(thisClass, "callback", "()V");
       if (NULL == midCallBack) return;
       printf("In C, call back Java's callback()\n");
       // Call back the method (which returns void), baed on the Method ID
       env->CallVoidMethod(thisObj, midCallBack);

       jmethodID midCallBackStr = env->GetMethodID(thisClass,
                                   "callback", "(Ljava/lang/String;)V");
       if (NULL == midCallBackStr) return;
       printf("In C, call back Java's called(String)\n");
       jstring message = env->NewStringUTF("Hello from C");
       env->CallVoidMethod(thisObj, midCallBackStr, message);

       jmethodID midCallBackAverage = env->GetMethodID(thisClass,
                                      "callbackAverage", "(II)D");
       if (NULL == midCallBackAverage) return;
       jdouble average = env->CallDoubleMethod(thisObj, midCallBackAverage, 2, 3);
       printf("In C, the average is %f\n", average);

       jmethodID midCallBackStatic = env->GetStaticMethodID(thisClass,
                                     "callbackStatic", "()Ljava/lang/String;");
       if (NULL == midCallBackStatic) return;
       jstring resultJNIStr = (jstring)env->CallStaticObjectMethod(thisClass, midCallBackStatic);
       const char *resultCStr = env->GetStringUTFChars(resultJNIStr, NULL);
       if (NULL == resultCStr) return;
       printf("In C, the returned string is %s\n", resultCStr);
       env->ReleaseStringUTFChars(resultJNIStr, resultCStr);
}

static JNINativeMethod methods[] = {
    {"nativeMethod", "()V", (void *)nativeMethod},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

核心步骤就是:

  1. 得到这个对象的类引用 GetObjectClass

  2. GetMethodID来得到method Id,需要提供签名,签名为:(参数)返回值

  3. 根据methodId调用函数 Call

    注意函数的区分是根据函数的返回值决定的

    参数就是在方法ID之后追加

  4. 静态的区别就是换了函数

    use GetStaticMethodID()得到Id

    调用使用CallStatic

  5. 函数原型是

    jmethodID GetMethodID(JNIEnv *env, jclass cls, const char *name, const char *sig);
       // Returns the method ID for an instance method of a class or interface.
       
    NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
    NativeType Call<type>MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
    NativeType Call<type>MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
       // Invoke an instance method of the object.
       // The <type> includes each of the eight primitive and Object.
       
    jmethodID GetStaticMethodID(JNIEnv *env, jclass cls, const char *name, const char *sig);
       // Returns the method ID for an instance method of a class or interface.
       
    NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
    NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
    NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
       // Invoke an instance method of the object.
       // The <type> includes each of the eight primitive and Object.

7. 调用对象父类的方法

  1. 得到对应的method Id, GetMethodID()

  2. 调用父类函数

    NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, ...);
    NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, const jvalue *args);
    NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, va_list args);

8. 在Native中调用对象构造器

java代码

public class Main {
    static {
        System.loadLibrary("hello");
    }
    private native Integer getIntegerObject(int number);

    public static void main(String[] args) {
        Main obj = new Main();
        System.out.println("In Java, the number is :" + obj.getIntegerObject(9999));
    }
}

native代码

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

jobject getIntegerObject(JNIEnv * env, jobject thisObj, jint number) {
   // Get a class reference for java.lang.Integer
    jclass cls = env->FindClass("java/lang/Integer");

    // Get the Method ID of the constructor which takes an int
    jmethodID midInit = env->GetMethodID(cls, "<init>", "(I)V");
    if (NULL == midInit) return NULL;
    // Call back constructor to allocate a new instance, with an int argument
    jobject newObj = env->NewObject(cls, midInit, number);

    // Try running the toString() on this newly create object
    jmethodID midToString = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
    if (NULL == midToString) return NULL;
    jstring resultStr = (jstring)env->CallObjectMethod(newObj, midToString);
    const char *resultCStr = env->GetStringUTFChars(resultStr, NULL);
    printf("In C: the number is %s\n", resultCStr);

    //May need to call releaseStringUTFChars() before return
    return newObj;
}

static JNINativeMethod methods[] = {
    {"getIntegerObject", "(I)Ljava/lang/Integer;", (void *)getIntegerObject},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

这种方式可以针对当你的native方法的返回值是一个自定义类型的时候

  1. 先根据 类名得到 要构建类的 class引用
  2. GetMethodID得到方法ID,这里要得到的是构造函数 名称就是"
  3. 建立在method Id基础上NewObject 来创建新对象

对应方法原型

jclass FindClass(JNIEnv *env, const char *name);
 
jobject NewObject(JNIEnv *env, jclass cls, jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
   // Constructs a new Java object. The method ID indicates which constructor method to invoke
 
jobject AllocObject(JNIEnv *env, jclass cls);
  // Allocates a new Java object without invoking any of the constructors for the object.

9. 数组对象的创建

java方法

public class Main {
    static {
        System.loadLibrary("hello");
    }
    private native Double[] sumAndAverage(Integer[] numbers);
    public static void main(String[] args) {
        Integer[] numbers = {11, 22, 32};  // auto-box
        Double[] results = new Main().sumAndAverage(numbers);
        System.out.println("In Java, the sum is " + results[0]);  // auto-unbox
        System.out.println("In Java, the average is " + results[1]);
    }
}

native方法

#include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C++ standard IO header
#include <string>    // C++ standard IO header
#include <cstdio>    // C++ standard IO header
#ifdef __cplusplus
extern "C" {
#endif

static const char *classPathName = "com/company/Main";

jobjectArray sumAndAverage(JNIEnv * env, jobject thisObj, jobjectArray inJNIArray) {
// Get a class reference for java.lang.Integer
   jclass classInteger = env->FindClass("java/lang/Integer");
   // Use Integer.intValue() to retrieve the int
   jmethodID midIntValue = env->GetMethodID(classInteger, "intValue", "()I");
   if (NULL == midIntValue) return NULL;

   // Get the value of each Integer object in the array
   jsize length = env->GetArrayLength(inJNIArray);
   jint sum = 0;
   int i;
   for (i = 0; i < length; i++) {
      jobject objInteger = env->GetObjectArrayElement(inJNIArray, i);
      if (NULL == objInteger) return NULL;
      jint value = env->CallIntMethod(objInteger, midIntValue);
      sum += value;
   }
   double average = (double)sum / length;
   printf("In C, the sum is %d\n", sum);
   printf("In C, the average is %f\n", average);

   // Get a class reference for java.lang.Double
   jclass classDouble = env->FindClass("java/lang/Double");

   // Allocate a jobjectArray of 2 java.lang.Double
   jobjectArray outJNIArray = env->NewObjectArray(2, classDouble, NULL);

   // Construct 2 Double objects by calling the constructor
   jmethodID midDoubleInit = env->GetMethodID(classDouble, "<init>", "(D)V");
   if (NULL == midDoubleInit) return NULL;
   jobject objSum = env->NewObject(classDouble, midDoubleInit, (double)sum);
   jobject objAve = env->NewObject(classDouble, midDoubleInit, average);
   // Set to the jobjectArray
   env->SetObjectArrayElement(outJNIArray, 0, objSum);
   env->SetObjectArrayElement(outJNIArray, 1, objAve);

   return outJNIArray;
}

static JNINativeMethod methods[] = {
    {"sumAndAverage", "([Ljava/lang/Integer;)[Ljava/lang/Double;", (void *)sumAndAverage},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL)
        return JNI_FALSE;

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    {
	//LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    std::cout << "Entering JNI_OnLoad" << std::endl;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }

    if ( env == NULL ) return -1;
    if (!registerNatives(env))
        return -1;

    return JNI_VERSION_1_4;
}


#ifdef __cplusplus
}
#endif

需要注意的是针对对象数组GetObjectArrayElement来得到某一个下标索引的对象

使用SetObjectArrayElement来替换对应位置的值

函数原型:

jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
   // Constructs a new array holding objects in class elementClass.
   // All elements are initially set to initialElement.
 
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
   // Returns an element of an Object array.
 
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
   // Sets an element of an Object array.