JNI

JNI 静态注册和动态注册,目的是让Java的native函数找到对应的c、c++函数
一、静态注册
1)生成头文件 javah

javah -classpath src/ -d jni/ -jni 包名.类名

package com.test.bbb;

public class JniTest{
    static {
        System.loadLibrary("JNI_ANDROID_TEST");
    }

    public static native String getObjectName(Object o);
    public static native String getTitleName();
    public static native int getAge(Object o);
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_bbb_JniTest */

#ifndef _Included_com_test_bbb_JniTest
#define _Included_com_test_bbb_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_test_bbb_JniTest
 * Method:    getObjectName
 * Signature: (Ljava/lang/Object;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_test_bbb_JniTest_getObjectName
  (JNIEnv *, jclass, jobject);

/*
 * Class:     com_test_bbb_JniTest
 * Method:    getTitleName
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_test_bbb_JniTest_getTitleName
  (JNIEnv *, jclass);

/*
 * Class:     com_test_bbb_JniTest
 * Method:    getAge
 * Signature: (Ljava/lang/Object;)I
 */
JNIEXPORT jint JNICALL Java_com_test_bbb_JniTest_getAge
  (JNIEnv *, jclass, jobject);

#ifdef __cplusplus
}
#endif
#endif

2)编写JNI层

上一步在jni目录里生成com_test_bbb_JniTest.h头文件,新建C+文件,后缀cpp,

include头文件,这里可以C++再调回java层代码。

#include <stdio.h>
#include <jni.h>
#include <JNIHelp.h>
#include <stdlib.h>
#include <android/log.h>
#include <string.h>
#include <com_test_bbb_JniTest.h>
#define UNUSED(x) (void)x

#ifndef LOG_TAG
#define LOG_TAG "jni_zxz"
#endif

#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

JNIEXPORT jstring JNICALL Java_com_test_bbb_JniTest_getObjectName (JNIEnv *env, jclass clazz, jobject obj) {
    UNUSED(clazz);
    jclass native_class = env->GetObjectClass(obj);
    jmethodID methodId = env->GetMethodID(native_class, "getPackageName", "()Ljava/lang/String;");
    jstring packageName = static_cast<jstring>(env->CallObjectMethod(obj, methodId));
    LOGD("Java_com_test_bbb_JniTest_getObjectName");
    return packageName;
}

JNIEXPORT jstring JNICALL Java_com_test_bbb_JniTest_getTitleName (JNIEnv *env, jclass clazz) {
    UNUSED(clazz);
    jstring title = env->NewStringUTF("NULL");
    jclass native_class = env->FindClass("com/test/bbb/People");
    LOGD("Java_com_test_bbb_JniTest_getTitleName start");
    jmethodID methodId = env->GetStaticMethodID(native_class, "getPeopleName", "()Ljava/lang/String;");
    title = static_cast<jstring>(env->CallStaticObjectMethod(native_class, methodId));
    LOGD("Java_com_test_bbb_JniTest_getTitleName end");

    return title;
}

JNIEXPORT jint JNICALL Java_com_test_bbb_JniTest_getAge (JNIEnv *env, jclass clazz, jobject obj) {
    UNUSED(clazz);
    jclass native_class = env->GetObjectClass(obj);
    jfieldID fieldId = env->GetStaticFieldID(native_class, "age", "I");
    jint age = env->GetStaticIntField(clazz, fieldId);
    LOGD("Java_com_test_bbb_JniTest_getAge %d", age);
    return age;
}
package com.test.bbb;

public class People {
    public People() {}
    private static String name = "haha";
    public static int age = 55;

    public static String getPeopleName() {
        return name;
    }
}

3)编译jni
make文件编译模块JNI_ANDROID_TEST
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := JNI_ANDROID_TEST

LOCAL_LDLIBS := -lm -llog

LOCAL_SRC_FILES := com_test_bbb_JniTest.cpp

include $(BUILD_SHARED_LIBRARY)

# This finds and builds the test apk as well, so a single make does both.
#include $(call all-makefiles-under,$(LOCAL_PATH))

遇到的问题有:

1)undefined reference to `__android_log_print' 的解决办法

a.在mk文件里加入
LOCAL_LDLIBS    := -lm -llog 
b.文件头部引入
#include <android/log.h>
c.宏定义
#define LOG_TAG "Native"

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

2)找不到so文件,AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/system/priv-app/BBB/BBB.apk"],nativeLibraryDirectories=[/system/priv-app/BBB/lib/arm64, /system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]] couldnt find "libJNI_ANDROID_TEST.so"

adb push <out>/system/lib64/JNI_ANDROID_TEST.so system/priv-app/BBB/lib/arm64
<out>/system/lib64/JNI_ANDROID_TEST.so system/lib64/

mv JNI_ANDROID_TEST.so libJNI_ANDROID_TEST.so

3) C++调用java层方法,GetStaticMethodID 静态方法找不到,但是其他方法和静态字段能够找到,然后确定是在编译的时候优化掉了。只需要在java代码中调用一下静态方法就可以找到。

二、动态注册

JNINativeMethod的结构来保存(Java native函数与JNI函数的)一一对应关联关系。
当Java层通过System.loadLibrary加载完JNI动态库后,就会查找该库中一个叫JNI_OnLoad的函数。
如果有就调用它,而动态注册的工作就是在这里完成的。

在system.load时就会去掉JNI_OnLoad,有就注册,没就不注册。

需要重写下如下方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    JNIEnv* env = NULL;
    jint result = -1;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    if (register_android_media_MediaScanner(env) < 0) {
        ALOGE("ERROR: MediaScanner native registration failed\n");
        goto bail;
    }
    /* success -- return valid version number */
    result = JNI_VERSION_1_4;
bail:
    return result;
}
1)导入包以及定义log
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <Android/log.h>
#define TAG "result" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

2)添加JNINativeMethod方法
static const JNINativeMethod gMethods[] = {
        {"getObjectName", "(Ljava/lang/Object;)Ljava/lang/String;", (jstring*)com_test_bbb_JniTest_getObjectName},
        {"getTitleName","()Ljava/lang/String;",(jstring*)com_test_bbb_JniTest_getTitleName}
};
3)Jni_OnLoad中注册

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)


JNI层函数的参数,第一个参数是JNIEnv,第二个参数jobject代表Java层的调用对象。
JNIEnv 是一个与线程相关的代表JNI环境的结构体。提供了一些JNI系统函数,通过这些函数可以做到:调用Java函数和操作jobject对象等很多事情。
JavaVM的AttachCurrentThread函数,获取线程的JNIEnv结构体,用来回调Java函数。
后台线程退出前,需要调用JavaVM的DetachCurrentThread函数释放对应的资源。

1.通过JNIEnv操作jobject



JNI基本类型数组
1.获取JNI基本类型数组元素
Get<Type>ArrayElements函数用来获取基本类型JNI数组的元素,这里面的<Type>需要被替换成实际的类型,比如GetIntArrayElements,GetLongArrayElements等
eg: jint *intArray=env->GetIntArrayElements(array,NULL);;//JNIEnv* env, jobject thiz,jintArray array


2.获取JNI基本类型数组的子数组
Get<Type>ArrayRegion函数用来获取JNI数组的子数组,这里面的<Type>需要被替换成实际的类型,比如GetIntArrayRegion,GetLongArrayRegion等
eg: env->GetIntArrayRegion(array,0,3,subArray);//JNIEnv* env, jobject thiz,jintArray array


3.设置JNI基本类型数组的子数组
Set<Type>ArrayRegion函数用来获取JNI基本类型数组的子数组,这里面的<Type>需要被替换成实际的类型,比如SetIntArrayRegion,SetLongArrayRegion等
eg: SetIntArrayRegion(array,0,3,subArray);//JNIEnv* env, jobject thiz,jintArray array


4.JNI对象数组
GetObjectArrayElement函数用来获取JNI对象数组元素
SetObjectArrayElement函数用来设置JNI对象数组元素
eg: //JNIEnv* env, jobject thiz,jobjectArray array
    int len=env->GetArrayLength(array);
    for(int i=0;i<len;i++)
    {
        jobject item=env->GetObjectArrayElement(array,i);
    }


5.获取JNI数组的长度
GetArrayLength用来获取数组的长度
eg: int len=env->GetArrayLength(array);//JNIEnv* env, jobject thiz,jobjectArray array


6.JNI NIO缓冲区相关的函数
使用NIO缓冲区可以在Java和JNI代码中共享大数据,性能比传递数组要快很多,当Java和JNI需要传递大数据时,推荐使用NIO缓冲区的方式来传递。
NewDirectByteBuffer函数用来创建NIO缓冲区
GetDirectBufferAddress函数用来获取NIO缓冲区的内容
GetDirectBufferCapacity函数用来获取NIO缓冲区的大小
eg:
const char *data="hello world";
    int len=strlen(data);
    jobject obj=env->NewDirectByteBuffer((void*)data,len);
    long capicity=env->GetDirectBufferCapacity(obj);
    char *data2=(char*)env->GetDirectBufferAddress(obj);


三、JNI访问Java类的方法和字段
1.Java类型签名映射表
JNI获取Java类的方法ID和字段ID,都需要一个很重要的参数,就是Java类的方法和字段的签名,这个签名需要通过下面的表来获取,这个表很重要,建议大家一定要记住。
Java类型签名
Boolean Z
Byte B
Char C
Short S
Integer I
Long J
Float F
Double D
Void V
任何Java类的全名 L任何Java类的全名;比如Java String类对应的签名是Ljava/lang/String;
type[] [这个就是Java数组的签名,比如Java int[]的签名是[I,Java long[]的签名就是[J,Java String[]的签名是 [Ljava/lang/String;
方法类型
(参数类型)返回值 类型,
比如Java方法void hello(String msg,String msg2)对应的签名就是(Ljava/lang/String; Ljava/lang/String;)V
再比如Java方法String getNewName(String name)对应的签名是(Ljava/lang/String;) Ljava/lang/String;
再比如Java方法long add(int a,int b)对应的签名是(II)J


四、JNI访问Java
jfieldId和jmethodId分别表示Java类的成员变量和成员函数。
通过GetFieldID和GetMethodID得到。


IsJavaInstanceOf(env, object, "java/lang/Float")


1.JNI访问Java类的实例方法
GetObjectClass函数用来获取Java对象对应的类类型
GetMethodID函数用来获取Java类实例方法的方法ID
Call<Type>Method函数用来调用Java类实例特定返回值的方法,比如CallVoidMethod,调用java没有返回值的方法,CallLongMethod用来调用Java返回值为Long的方法,等等。
Value result = MakeNullValue();
jmethodID method = env->GetMethodID(env->GetObjectClass(object), "booleanValue", "()Z");
result = MakeIntValue(env->CallBooleanMethod(object, method) == JNI_TRUE ? 1 : 0);


jmethodID method = env->GetMethodID(env->GetObjectClass(object), "floatValue", "()F");
result = MakeFloatValue(env->CallFloatMethod(object, method));


jclass clazz = env->FindClass("java/lang/Integer");
jmethodID constructorID = env->GetMethodID(clazz, "<init>", "(I)V");
result = env->NewObject(clazz, constructorID, GetIntValue(value));


FindClass是通过传java中完整的类名来查找java的class,
而GetObjectClass是通过传入jni中的一个java的引用来获取该引用的类型。


2.JNI访问Java类的静态方法
GetObjectClass函数用来获取Java对象对应的类类型
GetStaticMethodID函数用来获取Java类静态方法的方法ID
CallStatic<Type>Method函数用来调用Java类特定返回值的静态方法,比如CallStaticVoidMethod,调用java没有返回值的静态方法,CallStaticLongMethod用来调用Java返回值为Long的静态方法,等等。
eg: 
jmethodID methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");


3.JNI访问Java类字段相关的函数
//获取fieldID后,可调用Get<Type>Field系列函数获取jobject对应的成员变量的值。
NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID)
//或者调用Set<Type>Field系列函数来设置jobject对应的成员变量的值。
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value)


GetFieldID函数用来获取Java字段的字段ID
Get<Type>Field用来获取Java类字段的值,比如用GetIntField函数获取Java int型字段的值,用GetLongField函数获取Java long字段的值,用GetObjectField函数获取Java引用类型字段的值
eg:
jclass clazz=env->GetObjectClass(person);
    jfieldID name_fieldID=env->GetFieldID(clazz,"name","Ljava/lang/String;");
    jstring name=(jstring) env->GetObjectField(person, name_fieldID);


4.JNI访问Java类静态字段
GetStaticFieldID函数用来获取Java静态字段的字段ID
GetStatic<Type>Field用来获取Java类静态字段的值,比如用GetStaticIntField函数获取Java 静态int型字段的值,用GetStaticLongField函数获取Java 静态long字段的值,用GetStaticObjectField函数获取Java静态引用类型字段的值
eg:
jclass clazz=env->GetObjectClass(person);
    jfieldID name_fieldID=env->GetStaticFieldID(clazz,"name_static","Ljava/lang/String;");
    jstring name=(jstring) env->GetStaticObjectField(clazz,name_fieldID);


五、JNI线程同步相关的函数-JNI可以使用Java对象进行线程同步
MonitorEnter函数用来锁定Java对象
MonitorExit函数用来释放Java对象锁
eg:
env->MonitorEnter(obj);
    //do something
    env->MonitorExit(obj);


六、JNI处理Java异常
1.当JNI函数调用的Java方法出现异常的时候,并不会影响JNI方法的执行,但是我们并不推荐JNI函数忽略Java方法出现的异常继续执行,这样可能会带来更多的问题。我们推荐的方法是,当JNI函数调用的Java方法出现异常的时候,JNI函数应该合理的停止执行代码。
ExceptionOccurred函数用来判断JNI函数调用的Java方法是否出现异常
ExceptionClear函数用来清除JNI函数调用的Java方法出现的异常
eg:
env->CallVoidMethod(thiz,helloWorld_methodID);
    if(env->ExceptionOccurred()!=NULL){
        env->ExceptionClear();
        __android_log_print(ANDROID_LOG_VERBOSE,"hello","%s","program end with java exception");
        return;
    }


2.JNI抛出Java类型的异常
jclass clazz=env->FindClass("java/lang/NullPointerException");
    if(clazz==NULL) return;
    env->ThrowNew(clazz,"null pointer exception occurred");


七、JNI对象的全局引用和局部引用


对象的传递和操作

JNI中,C函数名的java对象参数,除了String类外则都表示为jobject类型(String类表示为jstring类型).
JNI提供了在本地代码中操作Java对象的功能。
基本原理
首先需要找到对象属于哪个类(class).类(class)在JNI中用jclass进行表示。
查找java类有两种方式
一、用类名 FindClass() 函数中查找得到jclass对象。
比如: jclass clazz = env->FindClass(包名类名) ;
二、 可以通过jclass  GetObjectClass  (jobject obj0)方法得到一个jclass,以表示obj0对象属于哪个类.
比如: jclass clazz=env->GetObjectClass (obj);
FindClass是通过传java中完整的类名来查找java的class,
而GetObjectClass是通过传入jni中的一个java的引用来获取该引用的类型。

然后对Field和函数再分别按下面的两种方式进行处理。
成员变量:通过GetFieldID/GetStaticFieldID得Field的id(以 jfieldID形式表示 ),然后调用GetXXXField/GetStaticXXXField函数就可以到Field的值,
调用SetXXXField/SetStaticXXXField就可以设置Field的值。
函数:和操作Field类似,通过GetMethodID/GetStaticMethodID得到函数的id(以jmethodID形式表示),然后就可以通过CallXXXMethod/CallStaticXXXMethod进行函数调用了.
GetFieldID/GetStaticFieldID简介
jfieldID GetFieldID (jclass cl0, const char * val1, const char * val2)
jfieldID GetStaticFieldID (jclass cl0, const char * val1, const char * val2)
前者用于非静态Field,后者用于静态Field
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,const char * val对应函数的名字
第三个参数,const char * val2用于表示Field是什么类型.

GetXXXField/GetStaticXXXField简介

GetXXXField/GetStaticXXXField中的XXX表示对什么类型的Field进行操作。 前者用于非静态Field,后者 用于静态Field。
该系列函数包括
非静态
  jobject GetObjectField (jobject obj0, jfieldID fld1)
  jboolean GetBooleanField (jobject obj0, jfieldID fld1)
  jbyte GetByteField (jobject obj0, jfieldID fld1)
  jchar GetCharField (jobject obj0, jfieldID fld1)
  jshort GetShortField (jobject obj0, jfieldID fld1)
  jint GetIntField (jobject obj0, jfieldID fld1)
  jlong GetLongField (jobject obj0, jfieldID fld1)
  jfloat GetFloatField (jobject obj0, jfieldID fld1)
  jdouble GetDoubleField (jobject obj0, jfieldID fld1)
静态
 jobject GetStaticObjectField (jclass cl0, jfieldID fld1)
 jboolean GetStaticBooleanField (jclass cl0, jfieldID fld1)
 jbyte GetStaticByteField (jclass cl0, jfieldID fld1)
 jchar GetStaticCharField (jclass cl0, jfieldID fld1)
 jshort GetStaticShortField (jclass cl0, jfieldID fld1)
 jint GetStaticIntField (jclass cl0, jfieldID fld1)
 jlong GetStaticLongField (jclass cl0, jfieldID fld1)
 jfloat GetStaticFloatField (jclass cl0, jfieldID fld1)
 jdouble GetStaticDoubleField (jclass cl0, jfieldID fld1)
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,表示GetFieldID/GetStaticFieldID中得到的Field的id
SetXXXField/SetStaticXXXField简介
SetXXXField/SetStaticXXXField中的XXX表示对什么类型的Field进行操作。 前者用于非静态Field,后者 用于静态Field。
该系列函数包括
非静态
  void SetObjectField (jobject obj0, jfieldID fld1, jobject obj2)
  void SetBooleanField (jobject obj0, jfieldID fld1, jboolean val2)
  void SetByteField (jobject obj0, jfieldID fld1, jbyte val2)
  void SetCharField (jobject obj0, jfieldID fld1, jchar val2)
  void SetShortField (jobject obj0, jfieldID fld1, jshort val2)
  void SetIntField (jobject obj0, jfieldID fld1, jint val2)
  void SetLongField (jobject obj0, jfieldID fld1, jlong val2)
  void SetFloatField (jobject obj0, jfieldID fld1, jfloat val2)
  void SetDoubleField (jobject obj0, jfieldID fld1, jdouble val2)
静态
  void SetStaticObjectField (jclass cl0, jfieldID fld1, jobject obj2)
  void SetStaticBooleanField (jclass cl0, jfieldID fld1, jboolean val2)
  void SetStaticByteField (jclass cl0, jfieldID fld1, jbyte val2)
  void SetStaticCharField (jclass cl0, jfieldID fld1, jchar val2)
  void SetStaticShortField (jclass cl0, jfieldID fld1, jshort val2)
  void SetStaticIntField (jclass cl0, jfieldID fld1, jint val2)
  void SetStaticLongField (jclass cl0, jfieldID fld1, jlong val2)
  void SetStaticFloatField (jclass cl0, jfieldID fld1, jfloat val2)
  void SetStaticDoubleField (jclass cl0, jfieldID fld1, jdouble val2)
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,表示GetFieldID/GetStaticFieldID中得到的Field的id
第三个参数,表示新值
GetMethodID/GetStaticMethodID简介
jmethodID GetMethodID (jclass cl0, const char * val1, const char * val2)
jmethodID GetStaticMethodID (jclass cl0, const char * val1, const char * val2)
调用他们能得到函数的id(以jmethodID形式表示).前者用于非静态Field,后者用于静态Field
第一个参数,jcalss cl0用于表示在哪个类上进行操作。
第二个参数,const char * val对应函数的名字
第三个参数,const char * val2用于表示函数的传入参数都有哪些,都是些什么类型,返回参数是什么类型。因为函数可以重载,所以必须要该参数才能定为函数。这里是以符号的形式表示传入参数的类型。

CallXXXMethod/CallStaticXXXMethod
CallXXXMethod/CallStaticXXXMethod中 的XXX表示对什么返回类型的Field函数进行调用。 前者用于非静态函数的调用,后者 用于静态函数的调用。
该系列函数包括
非静态
  jobject CallObjectMethod (jobject obj0, jmethodID meth1, ...)
  jboolean CallBooleanMethod (jobject obj0, jmethodID meth1, ...)
  jbyte CallByteMethod (jobject obj0, jmethodID meth1, ...)
  jchar CallCharMethod (jobject obj0, jmethodID meth1, ...)
  jshort CallShortMethod (jobject obj0, jmethodID meth1, ...)
  jint CallIntMethod (jobject obj0, jmethodID meth1, ...)
  jlong CallLongMethod (jobject obj0, jmethodID meth1, ...)
  jfloat CallFloatMethod (jobject obj0, jmethodID meth1, ...)
  jdouble CallDoubleMethod (jobject obj0, jmethodID meth1, ...)
  void CallVoidMethod (jobject obj0, jmethodID meth1, ...)
静态
  jobject CallStaticObjectMethod (jclass cl0, jmethodID meth1, ...)
  jboolean CallStaticBooleanMethod (jclass cl0, jmethodID meth1, ...)
  jbyte CallStaticByteMethod (jclass cl0, jmethodID meth1, ...)
  jchar CallStaticCharMethod (jclass cl0, jmethodID meth1, ...)
  jshort CallStaticShortMethod (jclass cl0, jmethodID meth1, ...)
  jint CallStaticIntMethod (jclass cl0, jmethodID meth1, ...)
  jlong CallStaticLongMethod (jclass cl0, jmethodID meth1, ...)
  jfloat CallStaticFloatMethod (jclass cl0, jmethodID meth1, ...)
  jdouble CallStaticDoubleMethod (jclass cl0, jmethodID meth1, ...)
  void CallStaticVoidMethod (jclass cl0, jmethodID meth1, ...)
第一个参数,jobject obj0表示调用哪个对象的非静态函数;jcalss cl0表示调用哪个类的静态函数。
第二个参数,jmethodID meth1表示调用函数的id(以jmethodID形式进行表示)
第三个及以后的参数,他们是调用的java函数的传入参数
如何返回一个新的JAVA对象呢?
通过NewObject函数创建一个java对象,然后像操作一般的对象一样操作它,最后返回该对象就可以了。
jobject NewObject (jclass cl0, jmethodID meth1, ...)
第一个参数,jclass cl0表示创建哪个类的实例。
第二个参数,jmethodID meth1表示用哪个构造函数创建该类的实例。
可用如下的方法得到构造函数的Id:
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值