美文网首页
JNI语法小结

JNI语法小结

作者: hongzhenw | 来源:发表于2018-05-02 18:02 被阅读0次

    JNI是什么?

    JNI的全称就是Java Native Interface,顾名思义,就是Java和C/C++相互通信的接口,就好比买卖房子都需要找中介一样,这里的JNI就是Java和C/C++通信的中介,一个中间人。

    JNI头文件

    JNI开发前提是要引入jni.h头文件,这个文件Android NDK目录下面。示例如下:

    include<jni.h>
    

    怎么加载so库?Android提供了3个实用的函数用来加载JNI库,分别是

    System.loadLibrary(libname)
    Runtime.getRuntime().loadLibrary(libname)
    Runtime.getRuntime().load(libFilePath)
    

    用loadLibrary函数加载

    用System.loadLibrary(libname)和Runtime.getRuntime().loadLibrary(libname)这两个函数加载so库,不需要指定so库的路径,Android会默认从系统的共享库目录里面去查找,Android的共享库目录就是vendor/lib和system/lib,如果在共享库路径里面找到指定名字的so库,就会立即加载这个so库,所以我们给so库起名的时候要尽量避免和Android共享库里面的so库同名。如果在共享库目录里面查找不到,就会在APP的安装目录里面查找APP的私有so库,如果查找到,会立即加载这个so库。

    用load函数加载

    Runtime.getRuntime().load(libFilePath)用这个函数加载so库,需要指定完整的so库路径,优点是加载速度快,并且不会加载错误的so库,缺点就是需要指定完整的so库路径,有时候并不方便,大家常用的加载so库的方式还是用loadLibrary函数来加载。

    加载so库示例

    static {
        System.loadLibrary("native-lib");
        //用这种方式加载so库和System.loadLibrary函数加载so库的效果是一样的
        //Runtime.getRuntime().loadLibrary("native-lib");
        //String soLibFilePath;
        //用这种方式加载so库需要指定完整的so库路径
        //Runtime.getRuntime().load(soLibFilePath);
    }
    
    Android Studio so库配置

    Android Studio通过CMakeLists.txt文件配置需要生成的so库,下面详细介绍一下这个CMakeLists.txt文件如何配置。

    Android Studio通过cmake命令来生成so库。

    CMakeLists.txt文件配置详解

    add_library

    add_library函数用来配置要生成的so库的基本信息,比如库的名字,要生成的so库是静态的还是共享的,so库的C/C++源文件列表

    示例如下:

    add_library( native-lib
                 SHARED
                 src/main/cpp/native-lib.cpp
                 src/main/cpp/native-lib2.cpp
                 src/main/cpp/native-lib3.cpp)
    

    第一个参数是so库的名字

    第二个参数是要生成的so库的类型,静态so库是STATIC,共享so库是SHARED

    第三个参数是C/C++源文件,可以包括多个源文件

    find_library

    find_library函数用来从NDK目录下面查找特定的so库。示例如下:

    find_library( log-lib
                  log )
    

    第一个参数是我们给要查找的so库起的名字,名字可以随便写

    第二个参数是要查找的so库的名字,这个名字是从真实的so库的名字去掉前缀和后缀后的名字,比如liblog.so这个so库的名字就是log

    target_link_libraries

    target_link_libraries函数用来把要生成的so库和依赖的其它so库进行链接,生成我们需要的so库文件。示例如下:

    target_link_libraries( native-lib
                           ${log-lib} )
    

    第一个参数是我们要生成的so库的名字去掉前缀和后缀后的名字,在这个例子中,要生成的真实的so库的名字是libnative-lib.so

    第二个参数是链接我们用find_library函数定义的查找的依赖库的名字log-lib,语法就是${依赖的库的名字}

    Java和JNI类型对照表

    这里详细介绍一下Java类型和C/C++类型的对照关系,方便我们下面的学习,这一部分知识很基础,也很重要。

    Java和JNI基本类型对照表

    Java的基本类型可以直接与C/C++的基本类型映射,因此Java的基本类型对开发人员是透明的。

    Java类型 本地类型 描述
    boolean jboolean C/C++8位整型
    byte jbyte C/C++带符号的8位整型
    char jchar C/C++无符号的16位整型
    short jshort C/C++带符号的16位整型
    int jint C/C++带符号的32位整型
    long jlong C/C++带符号的64位整型e
    float jfloat C/C++32位浮点型
    double jdouble C/C++64位浮点型
    Class jclass Class对象
    String jstring 字符串对象
    Object[] jobjectArray 任何对象的数组
    boolean[] jbooleanArray 布尔型数组
    byte[] jbyteArray 比特型数组
    char[] jcharArray 字符型数组
    short[] jshortArray 短整型数组
    int[] jintArray 整型数组
    long[] jlongArray 长整型数组
    float[] jfloatArray 浮点型数组
    double[] jdoubleArray 双浮点型数组
    Object jobject 任何Java对象,或者没有对应java类型的对象
    JNI字符串相关的函数

    C/C++字符串转JNI字符串

    NewString函数用来生成Unicode JNI字符串

    NewStringUTF函数用来生成UTF-8 JNI字符串

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
        char *str="helloboy";
        jstring jstr2=env->NewStringUTF(str);
    
        const jchar *jchar2=env->GetStringChars(jstr,NULL);
        size_t len=env->GetStringLength(jstr);
        jstring jstr3=env->NewString(jchar2,len);
    }
    

    JNI字符串转C/C++字符串

    GetStringChars函数用来从jstring获取Unicode C/C++字符串

    GetStringUTFChars函数用来从jstring获取UTF-8 C/C++字符串

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
        const char *str=env->GetStringUTFChars(jstr,NULL);
        const jchar *jchar2=env->GetStringChars(jstr,NULL);
    }
    

    释放JNI字符串

    ReleaseStringChars函数用来释放Unicode C/C++字符串

    ReleaseStringUTFChars函数用来释放UTF-8 C/C++字符串

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
        const char *str=env->GetStringUTFChars(jstr,NULL);
       env->ReleaseStringUTFChars(jstr,str);
        
        const jchar *jchar2=env->GetStringChars(jstr,NULL);
       env->ReleaseStringChars(jstr,jchar2);
    }
    

    JNI字符串截取

    GetStringRegion函数用来截取Unicode JNI字符串

    GetStringUTFRegion函数用来截取UTF-8 JNI字符串

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
        const char *str=env->GetStringUTFChars(jstr,NULL);
        char *subStr=new char;
        env->GetStringUTFRegion(jstr,0,3,subStr);
       env->ReleaseStringUTFChars(jstr,str);
    
        const jchar *jchar2=env->GetStringChars(jstr,NULL);
        jchar *subJstr=new jchar;
        env->GetStringRegion(jstr,0,3,subJstr);
       env->ReleaseStringChars(jstr,jchar2);
    }
    

    获取JNI字符串的长度

    GetStringLength用来获取Unicode JNI字符串的长度

    GetStringUTFLength函数用来获取UTF-8 JNI字符串的长度

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJString(JNIEnv* env, jobject thiz,jstring jstr) {
        jsize len=env->GetStringLength(jstr);
        jsize len2=env->GetStringUTFLength(jstr);
    }
    

    获取JNI基本类型数组元素

    Get<Type>ArrayElements函数用来获取基本类型JNI数组的元素,这里面的<Type>需要被替换成实际的类型,比如GetIntArrayElements,GetLongArrayElements等

    示例代码如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
        jint *intArray=env->GetIntArrayElements(array,NULL);
        int len=env->GetArrayLength(array);
        for(int i=0;i<len;i++){
            jint item=intArray[i];
        }
    }
    

    获取JNI基本类型数组的子数组

    Get<Type>ArrayRegion函数用来获取JNI数组的子数组,这里面的<Type>需要被替换成实际的类型,比如GetIntArrayRegion,GetLongArrayRegion等

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
        jint *subArray=new jint;
        env->GetIntArrayRegion(array,0,3,subArray);
    }
    

    设置JNI基本类型数组的子数组

    Set<Type>ArrayRegion函数用来获取JNI基本类型数组的子数组,这里面的<Type>需要被替换成实际的类型,比如SetIntArrayRegion,SetLongArrayRegion等

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
        jint *subArray=new jint;
        env->GetIntArrayRegion(array,0,3,subArray);
        env->SetIntArrayRegion(array,0,3,subArray);
    }
    

    JNI对象数组

    GetObjectArrayElement函数用来获取JNI对象数组元素

    SetObjectArrayElement函数用来设置JNI对象数组元素

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJObjectArray(JNIEnv* env, jobject thiz,jobjectArray array) {
        int len=env->GetArrayLength(array);
        for(int i=0;i<len;i++)
        {
            jobject item=env->GetObjectArrayElement(array,i);
        }
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJStringArray(JNIEnv* env, jobject thiz,jobjectArray array) {
        int len=env->GetArrayLength(array);
        for(int i=0;i<len;i++)
        {
            jstring item=(jstring)env->GetObjectArrayElement(array,i);
        }
    }
    
     
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJObjectArray(JNIEnv* env, jobject thiz,jobjectArray array) {
        jobject obj;
        env->SetObjectArrayElement(array,1,obj);
    }
    

    获取JNI数组的长度

    GetArrayLength用来获取数组的长度

    示例如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJObjectArray(JNIEnv* env, jobject thiz,jobjectArray array) {
        int len=env->GetArrayLength(array);
    }
     
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testJIntArray(JNIEnv* env, jobject thiz,jintArray array) {
        int len=env->GetArrayLength(array);
    }
    

    JNI NIO缓冲区相关的函数

    使用NIO缓冲区可以在Java和JNI代码中共享大数据,性能比传递数组要快很多,当Java和JNI需要传递大数据时,推荐使用NIO缓冲区的方式来传递。

    NewDirectByteBuffer函数用来创建NIO缓冲区

    GetDirectBufferAddress函数用来获取NIO缓冲区的内容

    GetDirectBufferCapacity函数用来获取NIO缓冲区的大小

    示例代码如下:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testDirectBuffer(JNIEnv* env, jobject thiz) {
        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类的方法和字段

    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[] 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类方法相关的函数

    JNI访问Java类的实例方法

    GetObjectClass函数用来获取Java对象对应的类类型

    GetMethodID函数用来获取Java类实例方法的方法ID

    Call<Type>Method函数用来调用Java类实例特定返回值的方法,比如CallVoidMethod,调用java没有返回值的方法,CallLongMethod用来调用Java返回值为Long的方法,等等。

    示例如下:

    Java代码:
    
    public native void callJavaHelloWorld2();
     
    public void helloWorld2(String msg){
        Log.i("hello","hello world "+msg);
    }
     
    JNI代码:
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld2(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorld2_methodID=env->GetMethodID(clazz,"helloWorld2","(Ljava/lang/String;)V");
        if(helloWorld2_methodID==NULL) return;
        const char *msg="hello world";
        jstring jmsg=env->NewStringUTF(msg);
        env->CallVoidMethod(thiz,helloWorld2_methodID,jmsg);
    }
    

    JNI访问Java类的静态方法

    GetObjectClass函数用来获取Java对象对应的类类型

    GetStaticMethodID函数用来获取Java类静态方法的方法ID

    CallStatic<Type>Method函数用来调用Java类特定返回值的静态方法,比如CallStaticVoidMethod,调用java没有返回值的静态方法,CallStaticLongMethod用来调用Java返回值为Long的静态方法,等等。

    示例如下:

    Java代码:
    
    public native void callStaticJavaHelloWorld2();
     
    public static void helloWorldStatic2(String msg){
        Log.i("hello","hello world static "+msg);
    }
    JNI代码:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld2(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorldStatic2_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic2","(java/lang/String;)V");
        if(helloWorldStatic2_methodID==NULL) return;
        const char *msg="hello world";
        jstring jmsg=env->NewStringUTF(msg);
        env->CallStaticVoidMethod(clazz,helloWorldStatic2_methodID,msg);
    }
    

    JNI访问Java类字段相关的函数

    JNI访问Java类实例字段

    GetFieldID函数用来获取Java字段的字段ID

    Get<Type>Field用来获取Java类字段的值,比如用GetIntField函数获取Java int型字段的值,用GetLongField函数获取Java long字段的值,用GetObjectField函数获取Java引用类型字段的值

    示例如下:

    Java代码:
    
    public class Person{
        public String name;
        public int age;
    }
    
    public native void getJavaObjectField(Person person);
     
    private void test(){
        Person person=new Person();
        person.name="wubb";
        person.age=20;
        getJavaObjectField(person);
    }
     
    
    JNI代码:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_getJavaObjectField(JNIEnv* env, jobject thiz,jobject person) {
        jclass clazz=env->GetObjectClass(person);
        jfieldID name_fieldID=env->GetFieldID(clazz,"name","Ljava/lang/String;");
        jstring name=(jstring) env->GetObjectField(person,name_fieldID);
    
        jfieldID age_fieldID=env->GetFieldID(clazz,"age","I");
        jint age=env->GetIntField(person,age_fieldID);
    }
    

    JNI访问Java类静态字段

    GetStaticFieldID函数用来获取Java静态字段的字段ID

    GetStatic<Type>Field用来获取Java类静态字段的值,比如用GetStaticIntField函数获取Java 静态int型字段的值,用GetStaticLongField函数获取Java 静态long字段的值,用GetStaticObjectField函数获取Java静态引用类型字段的值

    示例如下:

    Java代码:
    
    public class Person {
        public String name;
        public int age;
    
        public static String name_static;
        public static int age_static;
    }
     
    public native void getJavaObjectStaticField(Person person);
     
    private void test(){
        Person.name_static="wubb";
        Person.age_static=20;
    
        Person person=new Person();
        getJavaObjectStaticField(person);
    }
     
    JNI代码:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_getJavaObjectStaticField(JNIEnv* env, jobject thiz,jobject person) {
        jclass clazz=env->GetObjectClass(person);
        jfieldID name_fieldID=env->GetStaticFieldID(clazz,"name_static","Ljava/lang/String;");
        jstring name=(jstring) env->GetStaticObjectField(clazz,name_fieldID);
    
        jfieldID age_fieldID=env->GetStaticFieldID(clazz,"age_static","I");
        jint age=env->GetStaticIntField(clazz,age_fieldID);
    }
    

    JNI线程同步相关的函数

    JNI可以使用Java对象进行线程同步

    MonitorEnter函数用来锁定Java对象

    MonitorExit函数用来释放Java对象锁

    示例如下:

    Java代码:
    
    jniLock(new Object());
     
    JNI代码:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_jniLock(JNIEnv* env, jobject thiz,jobject obj) {
        env->MonitorEnter(obj);
        //do something
        env->MonitorExit(obj);
    }
    

    JNI异常相关的函数

    JNI处理Java异常

    当JNI函数调用的Java方法出现异常的时候,并不会影响JNI方法的执行,但是我们并不推荐JNI函数忽略Java方法出现的异常继续执行,这样可能会带来更多的问题。我们推荐的方法是,当JNI函数调用的Java方法出现异常的时候,JNI函数应该合理的停止执行代码。

    ExceptionOccurred函数用来判断JNI函数调用的Java方法是否出现异常

    ExceptionClear函数用来清除JNI函数调用的Java方法出现的异常

    请看如下示例:

    Java代码
    
    public void helloWorld(){
        throw new NullPointerException("null pointer occurred");
        //Log.i("hello","hello world");
    }
    C++代码
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorld_methodID=env->GetMethodID(clazz,"helloWorld","()V");
        if(helloWorld_methodID==NULL) return;
        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;
        }
        __android_log_print(ANDROID_LOG_VERBOSE,"hello","%s","program end normallly");
    }
    

    JNI抛出Java类型的异常

    JNI通过ThrowNew函数抛出Java类型的异常

    示例如下:

    Java代码
    
    try
    {
        testNativeException();
    }
    catch (NullPointerException e){
        e.printStackTrace();
    }
    C++代码
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testNativeException(JNIEnv* env, jobject thiz) {
        jclass clazz=env->FindClass("java/lang/NullPointerException");
        if(clazz==NULL) return;
        env->ThrowNew(clazz,"null pointer exception occurred");
    }
    

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

    我们知道Java代码的内存是由垃圾回收器来管理,而JNI代码则不受Java的垃圾回收器来管理,所以JNI代码提供了一组函数,来管理通过JNI代码生成的JNI对象,比如jobject,jclass,jstring,jarray等,对于这些对象,我们不能简单的在JNI代码里面声明一个全局变量,然后把JNI对象赋值给全局变量,我们需要采用JNI代码提供的专有函数来管理这些全局的JNI对象。

    JNI对象的局部引用

    在JNI接口函数中引用JNI对象的局部变量,都是对JNI对象的局部引用,一旦JNI接口函数返回,所有这些JNI对象都会被自动释放。不过我们也可以采用JNI代码提供的DeleteLocalRef函数来删除一个局部JNI对象引用。

    示例代码:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testDeleteLocalRef(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorld_methodID=env->GetMethodID(clazz,"helloWorld","()V");
        if(helloWorld_methodID==NULL) return;
       env->CallVoidMethod(thiz,helloWorld_methodID);
        env->DeleteLocalRef(clazz);
    }
    

    JNI对象的全局引用

    对于JNI对象,绝对不能简单的声明一个全局变量,在JNI接口函数里面给这个全局变量赋值这么简单,一定要使用JNI代码提供的管理JNI对象的函数,否则代码可能会出现预想不到的问题。JNI对象的全局引用分为两种,一种是强全局引用,这种引用会阻止Java的垃圾回收器回收JNI代码引用的Java对象,另一种是弱全局引用,这种全局引用则不会阻止垃圾回收器回收JNI代码引用的Java对象。

    强全局引用

    NewGlobalRef用来创建强全局引用的JNI对象

    DeleteGlobalRef用来删除强全局引用的JNI对象

    示例如下:

    jobject gThiz;
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testStrongGlobalRef(JNIEnv* env, jobject thiz) {
        //gThiz=thiz;//不能这样给全局JNI对象赋值,要采用下面这种方式
        gThiz=env->NewGlobalRef(thiz);//生成全局的JNI对象引用,这样生成的全局的JNI对象才可以在其它函数中使用
    
        env->DeleteGlobalRef(gThiz);//假如我们不需要gThiz这个全局的JNI对象引用,我们可以把它删除掉
    }
    

    弱全局引用

    NewWeakGlobalRef用来创建弱全局引用的JNI对象

    DeleteWeakGlobalRef用来删除弱全局引用的JNI对象

    IsSameObject用来判断两个JNI对象是否相同

    示例如下:

    jobject gThiz;
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_testWeakGlobalRef(JNIEnv*env, jobject thiz) {
        //gThiz=thiz;//不能这样给全局JNI对象赋值,要采用下面这种方式
        gThiz=env->NewWeakGlobalRef(thiz);//生成全局的JNI对象引用,这样生成的全局的JNI对象才可以在其它函数中使用
    
        if(env->IsSameObject(gThiz,NULL)){
            //弱全局引用已经被Java的垃圾回收器回收
        }
    
        env->DeleteWeakGlobalRef(gThiz);//假如我们不需要gThiz这个全局的JNI对象引用,我们可以把它删除掉
    }
    

    Java代码和JNI代码通信

    Java通过JNI接口调用C/C++方法
    首先我们需要在Java代码里面声明Native方法原型,比如:
    
    public native void helloJNI(String msg);
     
    其次我们需要在C/C++代码里面声明JNI方法原型,比如:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv* env, jobject thiz,jstring msg) {
        //do something
    }
    现在这段JNI函数声明代码采用的是C++语言写的,所以需要添加extern "C"声明,如果源代码是C语言,则不需要添加这个声明。
    
    JNIEXPORT 这个关键字说明这个函数是一个可导出函数,学过C/C++的朋友都知道,C/C++ 库里面的函数有些可以直接被外部调用,有些不可以,原因就是每一个C/C++库都有一个导出函数列表,只有在这个列表里面的函数才可以被外部直接调用,类似Java的public函数和private函数的区别。
    
    JNICALL 说明这个函数是一个JNI函数,用来和普通的C/C++函数进行区别,实际发现不加这个关键字,Java也是可以调用这个JNI函数的。
    
    Void 说明这个函数的返回值是void,如果需要返回值,则把这个关键字替换成要返回的类型即可。
    
    Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv*env, jobject thiz,jstring msg)这是完整的JNI函数声明,JNI函数名的原型如下:
    
    Java_ + JNI方法所在的完整的类名,把类名里面的”.”替换成”_” + 真实的JNI方法名,这个方法名要和Java代码里面声明的JNI方法名一样+ JNI函数必须的默认参数(JNIEnv* env, jobjectthiz)
    
    env参数是一个指向JNIEnv函数表的指针,
    
    thiz参数代表的就是声明这个JNI方法的Java类的引用
    
    msg参数就是和Java声明的JNI函数的msg参数对于的JNI函数参数
    
    JNI函数的原型
    
    [extern “C”]JNIEXPORT 函数返回值 JNICALL 完整的函数声明(JNIENV *env, jobject thiz, …)
    
    其中extern “C”根据需要动态添加,如果是C++代码,则必须要添加extern “C”声明,如果是C代码,则不用添加
    

    静态JNI方法和实例JNI方法区别

    先看一个示例:

    Java代码:
    
    public native void showHello();
    public native static void showHello2();
    C++代码:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_showHello(JNIEnv* env, jobject thiz) {
        //do something
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_showHello2(JNIEnv* env, jclass thiz) {
        //do something
    }
    
    相信明眼的同学很快就能发现这两个JNI函数的区别,对就是这个区别,普通的JNI方法对应的JNI函数的第二个参数是jobject类型,而静态的JNI方法对应的JNI函数的第二个参数是jclass类型
    

    常见的Java JNI方法声明和JNI函数声明示例

    Java Native方法声明:

    public class Person{
        public String name;
        public int age;
    }
    
    public native void helloJNI(String msg);
    public native int func1(int a,int b);
    public native String func2(String str);
    public native void func3(boolean b);
    public native void func4(Person person);
    public native static void func5();
     
    C++JNI函数声明:
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_helloJNI(JNIEnv* env, jobject thiz,jstring msg) {
        //do something
    }
    
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_func1(JNIEnv* env, jobject thiz,jint a,jint b) {
        //do something
    }
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_func2(JNIEnv* env, jobject thiz,jstring str) {
        //do something
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_func3(JNIEnv* env, jobject thiz,jboolean b) {
        //do something
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_func4(JNIEnv* env, jobject thiz,jobject person) {
        //do something
    }
     
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_func5(JNIEnv* env, jclass thiz) {
        //do something
    }
     
    所有的Java类对象在JNI函数里面都使用jobject来表示
    

    JNI代码和Java代码通信

    C++调用Java实例方法示例

    Java代码
    
    public native void callJavaHelloWorld();
    public native void callJavaHelloWorld2();
    public native void callJavaHelloWorld3();
     
    
    public void helloWorld(){
        Log.i("hello","helloworld");
    }
    
    public void helloWorld2(String msg){
        Log.i("hello","helloworld "+msg);
    }
    
    public void helloWorld3(inta,int b){
        int c=a+b;
        Log.i("hello","helloworld c="+c);
    }
    
    C++代码
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorld_methodID=env->GetMethodID(clazz,"helloWorld","()V");
        if(helloWorld_methodID==NULL) return;
        env->CallVoidMethod(thiz,helloWorld_methodID);
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld2(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorld2_methodID=env->GetMethodID(clazz,"helloWorld2","(java/lang/String;)V");
        if(helloWorld2_methodID==NULL) return;
        const char *msg="hello world";
        jstring jmsg=env->NewStringUTF(msg);
        env->CallVoidMethod(thiz,helloWorld2_methodID,jmsg);
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callJavaHelloWorld3(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorld3_methodID=env->GetMethodID(clazz,"helloWorld3","(II)V");
        if(helloWorld3_methodID==NULL) return;
        env->CallVoidMethod(clazz,helloWorld3_methodID,2,3);
    }
    C++调用Java静态方法示例
    
    Java代码
    
    public native void callStaticJavaHelloWorld();
    public native void callStaticJavaHelloWorld2();
    public native void callStaticJavaHelloWorld3();
    
    public static void helloWorldStatic(){
        Log.i("hello","helloworld static");
    }
    
    public static void helloWorldStatic2(String msg){
        Log.i("hello","helloworld static "+msg);
    }
    
    public static void helloWorldStatic3(inta,int b){
        int c=a+b;
        Log.i("hello","helloworld static c="+c);
    }
    
    C++代码
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorldStatic_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic","()V");
        if(helloWorldStatic_methodID==NULL) return;
        env->CallStaticVoidMethod(clazz,helloWorldStatic_methodID);
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld2(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorldStatic2_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic2","(java/lang/String;)V");
        if(helloWorldStatic2_methodID==NULL) return;
        const char *msg="hello world";
        jstring jmsg=env->NewStringUTF(msg);
        env->CallStaticVoidMethod(clazz,helloWorldStatic2_methodID,msg);
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_kgdwbb_jnistudy_MainActivity_callStaticJavaHelloWorld3(JNIEnv* env, jobject thiz) {
        jclass clazz=env->GetObjectClass(thiz);
        if(clazz==NULL) return;
        jmethodID helloWorldStatic3_methodID=env->GetStaticMethodID(clazz,"helloWorldStatic3","(II)V");
        if(helloWorldStatic3_methodID==NULL) return;
        env->CallStaticVoidMethod(clazz,helloWorldStatic3_methodID,2,3);
    }
    

    下载地址:Github

    相关文章

      网友评论

          本文标题:JNI语法小结

          本文链接:https://www.haomeiwen.com/subject/xxgcrftx.html