Java -- 常用的JNI接口函数简介(二)

Java -- 常用的JNI接口函数简介(二)

 

接着上一篇,再继续介绍一些其他常用的JNI接口函数(函数调用示例基于C++实现)。

 

一、数组操作

 

1、获取数据长度

在JNI中,如果我们需要获取常如数组参数的长度值,可以调用jsize GetArrayLength(JNIEnv *env, jarray array):

GetArrayLength
 
jsize GetArrayLength(JNIEnv *env, jarray array);
 
Returns the number of elements in the array.

PARAMETERS:
 
env: the JNI interface pointer.
 
array: a Java array object.
 
RETURNS:
 
Returns the length of the array.

返回值为数组的长度,示例:

static void android_animation_PropertyValuesHolder_callMultipleFloatMethod(
        JNIEnv* env, jclass pvhObject, jobject target, jlong methodID, jfloatArray arg)
{
    jsize parameterCount = env->GetArrayLength(arg);
    ...
}

2、创建数组

创建数组,需要调用NewObjectArray():

NewObjectArray
 
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.

PARAMETERS:
 
env: the JNI interface pointer.
 
length: array size.//需创建的数组大小
 
elementClass: array element class. //数组元素类型
 
initialElement: initialization value.//初始值,一般为NULL
 
RETURNS:
 
Returns a Java array object, or NULL if the array cannot be constructed.

例如,我们可以在JNI中创建一个数组,作为参数去调用Java层的某个方法。创建一个长度为array_size、元素类型为String的数组:

    jclass string_class = env->FindClass("java/lang/String");
    jobjectArray string_array = env->NewObjectArray(array_size, string_class, NULL);

3、获取/设置数组元素

操作数组元素是我们经常要使用的,JNI提供了两个函数让开发者操作数组:

GetObjectArrayElement
 
jobject GetObjectArrayElement(JNIEnv *env,
 jobjectArray array, jsize index);
 
Returns an element of an Object array.

PARAMETERS:
 
env: the JNI interface pointer.
 
array: a Java array.
 
index: array index.
 
RETURNS:
 
Returns a Java object.
 
THROWS:
 
ArrayIndexOutOfBoundsException: if index does not specify a valid index in the array.
SetObjectArrayElement
 
void SetObjectArrayElement(JNIEnv *env, jobjectArray array,
 jsize index, jobject value);
 
Sets an element of an Object array.

PARAMETERS:
 
env: the JNI interface pointer.
 
array: a Java array.
 
index: array index.
 
value: the new value.

GetObjectArrayElement()函数获取某个数组中、某个下标值的元素值;SetObjectArrayElement()设置某个数组、某个下标的元素值。

 

二、注册JNI函数

 

在Android中,我们实现了JNI函数之后,必须要对其进行注册,我们才能正常使用。JNI中注册native方法,需调用:

RegisterNatives
 
jint RegisterNatives(JNIEnv *env, jclass clazz,
 const JNINativeMethod *methods, jint nMethods);
 
Registers native methods with the class specified by the clazz argument. 
The methods parameter specifies an array of JNINativeMethod structures that contain the names, signatures
, and function pointers of the native methods. 
The name and signature fields of the JNINativeMethod structure are pointers to modified UTF-8 strings. 
The nMethods parameter specifies the number of native methods in the array. 
The JNINativeMethod structure is defined as follows:

typedef struct { 

    char *name; //Java中定义的方法名

    char *signature; //Java中方法的函数签名

    void *fnPtr; //函数指针,指向native函数;与参数name一一对应

} JNINativeMethod; 

 
The function pointers nominally must have the following signature:

ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...); 

PARAMETERS:
 
env: the JNI interface pointer.
 
clazz: a Java class object.//与该JNI文件相关联的Java类全限定名
 
methods: the native methods in the class.//需要注册的JNI方法数组
 
nMethods: the number of native methods in the class.//需注册的JNI方法的个数
 
RETURNS:
 
Returns “0” on success; returns a negative value on failure.

在调用该方法时,methods参数一般传递的是JNINativeMethod类型的数组,它包含了所有需要需要注册的JNI方法。
示例:需要注册的JNI方法:

JNINativeMethod gMethods[] = {
    {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;ILjava/lang/String;)J", (void *)add},
    {"nativeRemove", "(J)V", (void *)remove},
    {"nativeSetMode", "(I)V", (void *)setMode},
    {"nativeSendDtmf", "(I)V", (void *)sendDtmf},
};
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
jclass clazz;
    if ((clazz = env->FindClass("android/net/rtp/AudioGroup")) == NULL ||
        (gNative = env->GetFieldID(clazz, "mNative", "J")) == NULL ||
        (gMode = env->GetFieldID(clazz, "mMode", "I")) == NULL ||
        env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
        ALOGE("JNI registration failed");
        return -1;
    }
    return 0;

取消注册的函数较为简单:

UnregisterNatives
 
jint UnregisterNatives(JNIEnv *env, jclass clazz);
 
Unregisters native methods of a class. 

PARAMETERS:
 
env: the JNI interface pointer.
 
clazz: a Java class object.
 
RETURNS:
 
Returns “0” on success; returns a negative value on failure.

传递需要unregister的Java类的类对象即可。

 

三、C/C++JNI接口函数的调用形式

 

需要重申,native函数的C/C++实现,JNI函数调用的方式是不同的,看官方文档中的例子:

Native Method Arguments
 
The JNI interface pointer is the first argument to native methods. 
The JNI interface pointer is of type JNIEnv. The second argument differs depending on whether the native method is static or nonstatic. 
The second argument to a nonstatic native method is a reference to the object. 
The second argument to a static native method is a reference to its Java class.

The remaining arguments correspond to regular Java method arguments. 
The native method call passes its result back to the calling routine via the return value. 
 
Code Example 2-1 illustrates using a C function to implement the native method f. 
The native method f is declared as follows:

package pkg;  

class Cls { 

     native double f(int i, String s); 

     ... 

} 
The C function with the long mangled name Java_pkg_Cls_f_ILjava_lang_String_2 implements native method f:
 
Code Example 2-1 Implementing a Native Method Using C

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);

     /* process the string */
     ...

     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);

     return ...
}
Note that we always manipulate Java objects using the interface pointer env . 
Using C++, you can write a slightly cleaner version of the code, as shown in Code Example 2-2:
 
Code Example 2-2 Implementing a Native Method Using C++

extern "C" /* specify the C calling convention */  

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( 

     JNIEnv *env,        /* interface pointer */ 

     jobject obj,        /* "this" pointer */ 

     jint i,             /* argument #1 */ 

     jstring s)          /* argument #2 */ 

{ 

     const char *str = env->GetStringUTFChars(s, 0); 

     ... 

     env->ReleaseStringUTFChars(s, str); 

     return ... 

} 
With C++, the extra level of indirection and the interface pointer argument disappear from the source code.
However, the underlying mechanism is exactly the same as with C. 
In C++, JNI functions are defined as inline member functions that expand to their C counterparts.

从上述的官方示例中,我们可以看出:1、相对于C语言,C++实现中调用JNI接口函数少了一次解引用和参数传递;2、不管是C,还是C++形式的JNI调用,本质都是一样的。
至于JNI是用C,还是C++,可以根据自己的需求而定。Android framework中,JNI大部分都是C++实现的。





 

 

 

 

 

你可能感兴趣的:(编程开发杂类)