安卓实战开发之JNI再深入了解

JNI重新认识

头文件:

1.头文件中存放的是对某个库中所定义的函数、宏(define)、类型、全局变量等进行声明,它类似于一份仓库清单。若用户程序中需要使用某个库中的函数,则只需要将该库所对应的头文件include到程序中即可。

2.头文件中定义的是库中所有函数的函数原型。而函数的具体实现则是在库文件中。

3.在连接器连接程序时,会依据用户程序中导入的头文件,将对应的库函数导入到程序中。头文件以.h为后缀名。

头文件是给编译器用的,库文件是给连接器用的

函数库:

1.动态库:在编译用户程序时不会将用户程序内使用的库函数连接到用户程序的目标代码中,只有在运行时,且用户程序执行到相关函数时才会调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。

2.静态库:在编译用户程序时会将其内使用的库函数连接到目标代码中,程序运行时不再需要静态库。使用静态库生成可执行文件比较大。

为什么要进行交互?

首先,java语言提供的类库无法满足要求,且在数学运算,实时渲染的游戏上,音视频处理等方面上与c/c++相比效率稍低。然后,java语言无法直接操作硬件,c/c++代码不仅能操作硬件而且还能发挥硬件最佳性能。接着,使用java调用本地的c/c++代码所写的库,省去了重复开发的麻烦,并且可以利用很多开源的库提高程序效率。

java call c

Java调用C/C++大概有这样几个步骤

  1. 编写带有native方法的Java类, 使用javac工具编译Java类
  2. 使用javah来生成与native方法对应的头文件
  3. 实现相应的头文件, 并编译为动态链接库

我们对这个还是很清楚的,看代码:

c代码:

  //
    // Created by Administrator on 2016/8/1.
    //
    #include "JNIUtils.h"
    #include <string.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include <android/log.h>
    /**
     * 把一个jstring转换成一个c语言的char* 类型.
     */
    char* _JString2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env,"GB2312");
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
    jsize alen = (*env)->GetArrayLength(env, barr);
    jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
    if(alen > 0) {
    rtn = (char*)malloc(alen+1); //"\0"
    memcpy(rtn, ba, alen);
    rtn[alen]=0;
    }
    (*env)->ReleaseByteArrayElements(env, barr, ba,0);
    return rtn;
    }

    JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intMethod
                             (JNIEnv *env, jclass jobj,jint num){
                return num*num;
       }
    JNIEXPORT jboolean JNICALL Java_com_losileeya_jnimaster_JNIUtils_booleanMethod
                                 (JNIEnv * env, jclass jobj,jboolean boolean){
                return !boolean;
         }
    JNIEXPORT jstring JNICALL Java_com_losileeya_jnimaster_JNIUtils_stringMethod
                                (JNIEnv * env, jclass jobj,jstring jstr){
    //jstring jstr-->char*
    char* fromJava = _JString2CStr(env,jstr);
    char* fromC = "add I am from C!! ";
    //字符串的拼接函数,会把拼接后的结果放在第一个参数里面
    strcat(fromJava,fromC);

    return (*env)->NewStringUTF(env,fromJava);
        }

    JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intArrayMethod
          (JNIEnv * env, jclass jobj,jintArray array){
    int i, sum = 0;
    jsize len = (*env)->GetArrayLength(env, array);
    jint *body = (*env)->GetIntArrayElements(env, array, 0);

    for (i = 0; i < len; ++i)
    {
    sum += body[i];
    }
    (*env)->ReleaseIntArrayElements(env, array, body, 0);
    return sum;
    }

c++代码:

 /
    // Created by Administrator on 2016/8/1.
    //
    #include "JNIUtils.h"
    #include <string.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include <android/log.h>
    /**
     * 把一个jstring转换成一个c++语言的char* 类型.
     */
    char* _JString2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = env->FindClass( "java/lang/String");
    jstring strencode = env->NewStringUTF("GB2312");
    jmethodID mid = env->GetMethodID( clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); // String .getByte("GB2312");
    jsize alen = env->GetArrayLength( barr);
    jbyte* ba = env->GetByteArrayElements( barr, JNI_FALSE);
    if(alen > 0) {
    rtn = (char*)malloc(alen+1); //"\0"
    memcpy(rtn, ba, alen);
    rtn[alen]=0;
    }
    env->ReleaseByteArrayElements(barr, ba,0);
    return rtn;
    }

    JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intMethod(JNIEnv * env, jclass jobj,jint num){
    return num *num;
    }
    JNIEXPORT jboolean JNICALL Java_com_losileeya_jnimaster_JNIUtils_booleanMethod(JNIEnv * env, jclass jobj,jboolean boolean){
    return !boolean;
    }

    JNIEXPORT jstring JNICALL Java_com_losileeya_jnimaster_JNIUtils_stringMethod
            (JNIEnv *env , jclass jobj, jstring jstr){
    //jstring jstr-->char*
    char* fromJava = _JString2CStr(env,jstr);
    char* fromC = "add I am from C!! ";
    //字符串的拼接函数,会把拼接后的结果放在第一个参数里面
    strcat(fromJava,fromC);
    return env->NewStringUTF(fromJava);
    }
    JNIEXPORT jint JNICALL Java_com_losileeya_jnimaster_JNIUtils_intArrayMethod(JNIEnv * env, jclass jobj,jintArray array){
    int sum = 0;
    jsize len = env->GetArrayLength(array);
    jint *arr = env->GetIntArrayElements(array, 0);
    for(int i = 0;i<len; i++){
    sum+=arr[i];
    }
    env->ReleaseIntArrayElements(array, arr,0);
    return sum;
    }

从上面Native函数的命名上我们可以了解到JNI函数的命名规则: Java代码中的函数声明需要添加native 关键 字;Native的对应函数名要以“Java”开头,后面依次跟上Java的“package名”、“class名”、“函数名”,中间以下划线“” 分割,在package名中的“.”也要改为“_”。此外,关于函数的参数和返回值也有相应的规则。对于Java中的基本类型如int 、double 、char 等,在Native端都有相对应的类型来表示,如jint 、jdouble 、jchar 等;其他的对象类型则统统由jobject 来表示(String 是个例外,由于其使用广泛,故在Native代码中有jstring 这个类型来表示,正如在上例中返回值String 对应到Native代码中的返回值jstring )。而对于Java中的数组,在Native中由jarray 对应,具体到基本类型和一般对象类型的数组则有jintArray 等和jobjectArray 分别对应(String 数组在这里没有例外,同样用jobjectArray 表示)。还有一点需要注意的是,在JNI的Native函数中,其前两个参数JNIEnv 和jobject* 是必需的——前者是一个JNIEnv 结构体的指针,这个结构体中定义了很多JNI的接口函数指针,使开发者可以使用JNI所定义的接口功能;后者指代的是调用这个JNI函数的Java对象,有点类似于C++中的this 指针。在上述两个参数之后,还需要根据Java端的函数声明依次对应添加参数。在上例中,Java中声明的JNI函数没有参数,则Native的对应函数只有类型为JNIEnv 和jobject* 的两个参数。

效果图:

c call java

一般来说,要在Native代码中访问Java对象,有如下几个步骤:

  1. 得到该Java对象的类定义。JNI定义了jclass 这个类型来表示Java的类的定义,并提供了FindClass接口,根据类的完整的包路径即可得到其jclass 。
  2. 根据jclass 创建相应的对象实体,即jobject 。在Java中,创建一个新对象只需要使用new 关键字即可,但在Native代码中创建一个对象则需要两步:首先通过JNI接口GetMethodID得到该类的构造函数,然后利用NewObject接口构造出该类的一个实例对象。
  3. 访问jobject 中的成员变量或方法。访问对象的方法是先得到方法的Method ID,然后使用CallMethod 接口调用,这里Type对应相应方法的返回值——返回值为基本类型的都有相对应的接口,如CallIntMethod;其他的返回值(包括String) 则为CallObjectMethod。可以看出,创建对象实质上是调用对象的一个特殊方法,即构造函数。访问成员变量的步骤一样:首先 GetFieldID得到成员变量的ID,然后Get/SetField读/写变量值。

寻找class对象, 并实例化

JVM在Java中都是自己启动的, 在C/C++中只能自己来启动了, 启动完之后的事情就和在Java中一样了, 不过要使用C/C++的语法.

获取class对象比较简单, FindClass(env, className).

cls = (*env)->FindClass(env, "xxxx");

在Java中的类名格式是java.lang.String, 但是className的格式有点不同, 不是使用’.’作为分割, 而是’/’, 即java/lang/String.

我们知道Java中构造函数有两种, 一种是默认的没有参数的, 一种是自定义的带有参数的. 对应的在C/C++中, 有两种调用构造函数的方法.

调用默认构造函数

// 调用默认构造函数  obj = (*env)->AllocObjdect(env, cls);

构造函数也是方法, 类似调用方法的方式.

// 调用指定的构造函数, 构造函数的名字叫做<init>  mid = (*env)->GetMethodID(env, cls, "<init>", "()V");  obj = (*env)->NewObject(env, cls, mid);

调用方法和修改属性

关于方法和属性是有两个ID与之对应, 这两个ID用来标识方法和属性.

jmethodID mid;  jfieldID fid;

方法分为静态和非静态的, 所以对应的有

mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");     mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");

上面两个方法是同名的, 都叫sayHello, 但是签名不同, 所以可以区分两个方法.

JNI的函数都是有一定规律的, Static就表示是静态, 没有表示非静态.

方法的调用如下

jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);     jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);

我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.

属性也有静态和非静态, 示例中只有非静态的.

获取属性ID

fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");

改属性的值

(*env)->SetObjectField(env, obj, fid, arg); // 修改属性

关于jstring的说明

java的String都是使用了unicode, 是双字节的字符, 而C/C++中使用的单字节的字符。

从C转换为java的字符, 使用NewStringUTF方法

jstring arg = (*env)->NewStringUTF(env, name);

从java转换为C的字符, 使用GetStringUTFChars

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

下面我们来看代码:

c代码:

  /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_helloFromJava
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1helloFromJava
      (JNIEnv *env, jobject jobj){
        jclass jclazz=(*env)->FindClass(env,"com/losileeya/jnimaster/JNIUtils");
        jmethodID jmethodid=(*env)->GetMethodID(env,jclazz,"helloFromJava","()V");
        jobject jobjs=(*env)->AllocObject(env,jclazz);
        (*env)->CallVoidMethod(env,jobjs,jmethodid);
      }

    /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_add
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1add
      (JNIEnv *env, jobject jobj){
    //1.得到类对应的字节码
        //全类名,把.改成/
        //jclass      (*FindClass)(JNIEnv*, const char*);
        jclass jclazz = (*env)->FindClass(env, "com/losileeya/jnimaster/JNIUtils");

        //2.得到要调用的方法名
        //第三个参数:方法名
        //第四个但是:方法签名
        //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
        jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "add",
                "(II)I");

        //3.得到要调用的方法对应的类的实例
        // jobject     (*AllocObject)(JNIEnv*, jclass);
        jobject jobjs = (*env)->AllocObject(env, jclazz);
        //4.调用方法
        // jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
          int reuslt =  (*env)->CallIntMethod(env,jobjs,jmethodid,99,1);

      }

    /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_printString
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1printString
      (JNIEnv *env, jobject jobj){
     //1.得到类对应的字节码
            //全类名,把.改成/
            //jclass      (*FindClass)(JNIEnv*, const char*);
            jclass jclazz = (*env)->FindClass(env, "com/losileeya/jnimaster/JNIUtils");

            //2.得到要调用的方法名
            //第三个参数:方法名
            //第四个但是:方法签名
            //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
            jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "printString",
                    "(Ljava/lang/String;)V");

            //3.得到要调用的方法对应的类的实例
            // jobject     (*AllocObject)(JNIEnv*, jclass);
            jobject jobjs = (*env)->AllocObject(env, jclazz);
            //4.调用方法
            // void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
            jstring text = (*env)->NewStringUTF(env,"I am from C!!");
            (*env)->CallVoidMethod(env, jobjs, jmethodid,text); //成功调用了Java中JNI里面的printString(String s);
      }

    /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_sayHello
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1sayHello
      (JNIEnv * env, jobject jobj){
         //1.得到字节码
         jclass jclazz = (*env)->FindClass(env,"com/losileeya/jnimaster/JNIUtils");
         //2.得到方法
         jmethodID  jmethodid = (*env)->GetStaticMethodID(env,jclazz,"sayHello","(Ljava/lang/String;)V");

         //3.调用
         //void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
            jstring text = (*env)->NewStringUTF(env,"I am from C!! I am static method !!!");
         (*env)->CallStaticVoidMethod(env,jclazz,jmethodid,text);//成功调用了Java中JNI类的静态方法sayHello(String text)

      }

c++代码:

 /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_helloFromJava
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1helloFromJava(JNIEnv*env,jobject jobj){
    jclass jclazz = env->FindClass("com/losileeya/jnimaster/JNIUtils");
    jmethodID jmethodid = env->GetMethodID(jclazz, "helloFromJava", "()V");
    jobject jobjs = env->AllocObject(jclazz);
    env->CallVoidMethod(jobjs, jmethodid);
    }

    /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_add
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1add(JNIEnv*env,jobject jobj){
    //1.得到类对应的字节码
    //全类名,把.改成/
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = env->FindClass( "com/losileeya/jnimaster/JNIUtils");

    //2.得到要调用的方法名
    //第三个参数:方法名
    //第四个但是:方法签名
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID jmethodid = env->GetMethodID(jclazz, "add",
                                              "(II)I");

    //3.得到要调用的方法对应的类的实例
    // jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jobjs = env->AllocObject(jclazz);
    //4.调用方法
    // jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    int reusle =  env->CallIntMethod(jobjs,jmethodid,99,1);

    }

    /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_printString
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1printString(JNIEnv*env,jobject jobj){
    //1.得到类对应的字节码
    //全类名,把.改成/
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = env->FindClass( "com/losileeya/jnimaster/JNIUtils");

    //2.得到要调用的方法名
    //第三个参数:方法名
    //第四个但是:方法签名
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID jmethodid = env->GetMethodID( jclazz, "printString",
                                              "(Ljava/lang/String;)V");

    //3.得到要调用的方法对应的类的实例
    // jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jobjs = env->AllocObject(jclazz);
    //4.调用方法
    // void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    jstring text = env->NewStringUTF("I am from C!!");
    env->CallVoidMethod( jobjs, jmethodid,text); //成功调用了Java中JNI里面的printString(String s);
    }

    /*
     * Class:     com_losileeya_jnimaster_JNIUtils
     * Method:    ccallJava_sayHello
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jnimaster_JNIUtils_ccallJava_1sayHello(JNIEnv*env,jobject jobj){
    //1.得到字节码
    jclass jclazz = env->FindClass("com/losileeya/jnimaster/JNIUtils");
    //2.得到方法
    jmethodID  jmethodid = env->GetStaticMethodID(jclazz,"sayHello","(Ljava/lang/String;)V");

    //3.调用
    //void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    jstring text = env->NewStringUTF("I am from C!! I am static method !!!");
    env->CallStaticVoidMethod(jclazz,jmethodid,text);//成功调用了Java中JNI类的静态方法sayHello(String text)
    }

可以看到,上述代码和前面讲到的步骤完全相符。这里提一下编程时要注意的要点:1、FindClass要写明Java类的完整包路径,并将 “.”以“/”替换;2、GetMethodID的第三个参数是方法名(对于构造函数一律用“”表示),第四个参数是方法的“签 名”,需要用一个字符串序列表示方法的参数(依声明顺序)和返回值信息。由于篇幅所限,这里不再具体说明如何根据方法的声明构造相应的“签名”,请参考 JNI的相关文档。

关于上面谈到的步骤再补充说明一下:在JNI规范中,如上这种使用NewObject创建的对象实例被称为“Local Reference”,它仅在创建它的Native代码作用域内有效,因此应避免在作用域外使用该实例及任何指向它的指针。如果希望创建的对象实例在作用 域外也能使用,则需要使用NewGlobalRef接口将其提升为“Global Reference”——需要注意的是,当Global Reference不再使用后,需要显式的释放,以便通知JVM进行垃圾收集。

顺便看下截图:

JNI 更新UI

在Android使用Jni时,为了能够使UI线程即主线程与工作线程分开,经常要创建工作线程,然后在工作线程中调用C/C++函数.为了在C/C++ 函数中更新Android的UI,又时常使用回调。jni更新ui的话,我们就要注重jobject的使用了。

看代码:(使用)

 static {
            System.loadLibrary("CCallJavaForUI");
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
        public void CCallJavaForUI(View view){
            this.callShowToast();

        }

        public void showToast(){
            //this - Activity的实例
            //startActitity();-->
            //new MainActivity();
            System.out.println("showToast()----------");
            Toast.makeText(this, "showToast()---------", Toast.LENGTH_LONG).show();
        }

        /**
         * 调用MainActivity中的showToast()方法
         */
        public native void callShowToast();

c代码 :

    //
    // Created by Administrator on 2016/8/6.
    //
    #include "JNIUtils.h"
    #include<stdio.h>
    #include<stdlib.h>
    /**
     * 调用java 中MainActivity中的showToast()方法
     * jobject jobj:谁调用就是谁的实例,当前是JNI.this--->MainActivity.this
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jniupdateui_MainActivity_callShowToast
    (JNIEnv * env, jobject jobj){

    //1.得到字节码
    jclass   jclazz = (*env)->FindClass(env,"com/losileeya/jniupdateui/MainActivity");
    //2.得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID jmethodid = (*env)->GetMethodID(env,jclazz,"showToast","()V");
    //3.得到对象
    //      jobject jobjs = (*env)->AllocObject(env,jclazz);
    //4.调用方法
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,jobj,jmethodid);//成功调用了中MainActivity中的showToast()方法
    };

c++代码:

  //
    // Created by Administrator on 2016/8/6.
    //
    #include "JNIUtils.h"
    #include<stdio.h>
    #include<stdlib.h>
    /**
     * 调用java 中MainActivity中的showToast()方法
     * jobject jobj:谁调用就是谁的实例,当前是JNI.this--->MainActivity.this
     */
    JNIEXPORT void JNICALL Java_com_losileeya_jniupdateui_MainActivity_callShowToast
    (JNIEnv * env, jobject jobj){

    //1.得到字节码
    jclass   jclazz = (*env)->FindClass(env,"com/losileeya/jniupdateui/MainActivity");
    //2.得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID jmethodid = (*env)->GetMethodID(env,jclazz,"showToast","()V");
    //3.得到对象
    //      jobject jobjs = (*env)->AllocObject(env,jclazz);
    //4.调用方法
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,jobj,jmethodid);//成功调用了中MainActivity中的showToast()方法
    };

效果图:

C和C++函数时的JNI使用区别

Java调用C和C++函数时的JNI使用区别:

注意:jni.h头文件中对于.c & .cpp采用不同的定义

在C的定义中,env是一个两级指针,而在C++的定义中,env是个一级指针

C形式需要对env指针进行双重deferencing,而且须将env作为第一个参数传给jni函数

jclass (JNICALL *GetObjectClass) (JNIEnv *env, jobject obj);

jclass GetObjectClass(jobject obj)

{

return functions->GetObjectClass(this,obj);

}

对于*.c

1.jclass test_class = (*env)->GetObjectClass(env, obj);

2.jfieldID id_num = (*env)->GetFieldID(env, test_class, “num”, “I”);

对于 *.cpp

1.jclass test_class = env->GetObjectClass(obj);

2.jfieldID id_num = env->GetFieldID(test_class, “num”, “I”);

在 C 中,

JNI 函数调用由“(*env)->”作前缀,目的是为了取出函数指针所引用的值。

在 C++ 中,

JNIEnv 类拥有处理函数指针查找的内联成员函数。

下面将说明这个细微的差异,其中,这两行代码访问同一函数,但每种语言都有各自的语法。

C 语法:jsize len = (*env)->GetArrayLength(env,array);

C++ 语法:jsize len =env->GetArrayLength(array);

1、jni 可以调用本地C函数。
2、jni 调用C++库时,首先要将C++库提供的功能封装成纯C格式的函数接口,然后jni里面调用这些C接口。

总结,没什么区别。一个是 jni调用c。另一个是jni调用c,c调用c++。

传送门:jnimaster

总结

JNI使用c和cpp的基本使用和了解就讲的差不多了,更多的学习可以去看jni的使用安全手册。

时间: 2024-10-11 01:10:59

安卓实战开发之JNI再深入了解的相关文章

安卓实战开发之JNI入门及高效的配置(android studio一键生成.h,so及方法签名)

前言 以前也讲过NDK开发,但是开始是抱着好玩的感觉去开始的,然后呢会helloWord就觉得大大的满足,现在静下来想这NDK开发到底是干什么呢? NDK开发,其实是为了项目需要调用底层的一些C/C++的一些东西:另外就是为了效率更加高效些但是在java与C相互调用时平白又增大了开销(其实效率不见得有所提高),然后呢,基于安全性的考虑也是为了防止代码被反编译我们为了安全起见,使用C语言来编写这些重要的部分来增大系统的安全性,最后呢生成so库便于给人提供方便. 好了,我们来看一下qq的结构,我们就

安卓实战开发之CardView的selector及GrideView的item按下状态保留selector(state_activated)的实现

android的selector对于android开发者而言再熟悉不过了,只要定义一个drawable目录下定义一个selector的xml文件,在布局文件中background引用这个xml文件或者在代码中setBackgroundDrawable的时候使用此xml就可以实现控件按下或有焦点或激活状态等不同状态的效果.你真的对selector的使用全了解吗? 前言 项目中确实用到了像多个标签,然后选中规格要给文字的边框加点颜色,对于长度和个数不确定的情况呢我们通常可以在代码中通过数据的长度动态

安卓实战开发之SQLite从简单使用crud

前言 最近项目忙,然后呢很久没有更新博客了,react-native也是没有时间学习,然后项目里面用到了数据持久化(数据存储),Android系统中主要提供了三种数据持久化方式:文件存储.SharedPreference存储.数据库存储.说实在的毕竟app这种轻量级的使用数据库还是不多,然后呢要使用数据库也是在特定场合,这也导致了很多的移动端开发(对数据库操作不多)对数据库使用不太熟练. 应用场景 一般我们都不使用数据库的,基本上使用SharedPreference就能处理大部分问题,然后在特定

Android开发之JNI(一)--HelloWorld及遇到的错误解析

Android开发之JNI(一)--HelloWorld及遇到的错误解析 1.NDK环境搭建 參考http://blog.csdn.net/xiaoliouc/article/details/8705560 2.HelloWorld编写 (1)新建一个AndroidprojectJniDemo,这个名字能够随便起. (2)新建一个HelloWorld.java类,里面的内容例如以下: public class HelloWorld { public native String print();

Android开发之JNI调用本地C库专题(一):JNI的使用

JNI,是用于开发本地C函数库的技术.用于链接JAVA和C或者C++语言的桥梁.在部分android项目开发中,我们是需要用到这项技术的.那么废话不多说,进入正题. 开发JNI,需要用到NDK,这个大家应该都知道了.还需要一个linux的开发环境.一般而言,可以使用虚拟机装一个ubantu,博主以前就是搞linux开发的,这点还是比较熟悉.但是对于大部分android开发者而言,弄一个虚拟机成本太高.那么,我们需要搭建一个模拟linux的开发环境.这个博主就不说了,直接上链接 NDK环境搭建 以

Android NDK开发之Jni调用Java对象

https://my.oschina.net/zhiweiofli/blog/114064 通过使用合适的JNI函数,你可以创建Java对象,get.set 静态(static)和 实例(instance)的域,调用静态(static)和实例(instance)函数.JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数.下表列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数.每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对

安卓实践开发之MVP一步步实现到高级封装

在上家干了快2年辞职后在家休息了快一个月了,说实在的不上班的感觉爽(睡觉睡到自然醒,游戏玩到手抽筋).哈哈,又是快到一年过中秋的时候了,好久没有更新博客了,今天顺便撸一篇. 前言 话说MVP的模式已经问世好几年了,为什么很多公司还是不愿意接受呢?说实在的我就还是喜欢自己的mvc,不喜欢看见mvp庞大的架构,所以前公司的项目呢也不曾使用过mvp(同事也不接受这种模式),毕竟项目架构不是特别复杂的话使用mvp显示不出他的优势,相反给人的感觉是实现一个界面多出了很多的代码. 然而现在最火的应该是mvv

安卓混合开发之Cordova,NativeWebView两种实现

转载请注明出处:王亟亟的大牛之路 如今混合开发已经不是新鲜词了,虽然作为源生的死忠我不怎么愿意去用H5实现我的功能,但是需求说了算...还是屈服了...然后就去了解了下,也抠了点Demo在这里分享给大家(也许网上有类似的,但是我这个肯定是可以run并且实现方式是不同的) 上内容之前,先说下纯H5 混合 纯native的各种区别,不了解的可以看下下面的简单描述(扣来的) 一.原生应用 优点 可访问手机所有功能(GPS.摄像头): 速度更快.性能高.整体用户体验不错: 可线下使用(因为是在跟Web相

iOS开发之Socket通信实战--Request请求数据包编码模块

实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地 址:https://github.com/robbiehanson/CocoaAsyncSocket,读者可以自行google或者baidu搜索 这个库的用法,网上有很多资料,而且用法不难. 在一些对Socket通信使用需求不是很高的应用中,比如需要