• Android随笔-JNI


    概述

    请添加图片描述
    JNI:Java Native Interface,即Java本地接口,使Java与其他类型的语言进行交互,和C/C++交互的比较多。JNI属于Java的一部分,是JDK的组成部分,和Android关系不大,但是Android中核心的业务或高性能的功能都是C/C++开发的,比如游戏渲染、音视频编解码等,所以对于Android开发,JNI也是需要了解的。Android中并不是直接和C/C++打交道,是和.so(动态库),.a(静态库)文件打交道。

    创建

    Android中使用JNI需要提前安装NDK,NDK(Native Development Kit)是后台Android平台提供的开发工具包,把JNI、gcc、g++…等都封装进入了,方便开发者开发。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    project:
    请添加图片描述
    CMakeLists.txt:
    在这里插入图片描述
    native-lib.cpp
    在这里插入图片描述
    和常规的Android项目比,大部分都一样,有些配置文件不同,还多了cpp目录,cpp目录下native-lib.cpp文件就是Java调用C/C++的地方。
    CMakeLists.txt是CMake的组态档,组态档是用一种建构软件专用的特殊编程语言写的CMake脚本,CMake是CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。

    使用

    规则

    java中的属性和方法在C/C++中不能直接使用,需要按照一定的规则进行转换。

    javaC/C++
    booleanZ
    byteB
    charC
    shortS
    intI
    longJ
    floatF
    doubleD
    voidV
    object objLobj
    int[][I
    void name(double b)(D) V

    加载库

        // Used to load the 'jni' library on application startup.
        static {
            System.loadLibrary("jni");
        }
    
    • 1
    • 2
    • 3
    • 4

    如果没有设置库名称,可以直接使用文件名。

        static {
            System.loadLibrary("native-lib");
        }
    
    • 1
    • 2
    • 3

    创建native方法

    在MainActivity中创建native方法。

       /**
         * A native method that is implemented by the 'jni' native library,
         * which is packaged with this application.
         */
        public native String stringFromJNI();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    native-lib.cpp中需要使用c++实现创建的native方法。

    实现native方法

    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_jni_MainActivity_stringFromJNI(
            JNIEnv* env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    细节解读:

    • extern “C”
      表示下面的代码都会采用C的编译方式,因为native-lib是c++文件,所以在c++文件中使用C需要单独标记一下。如果代码使用C++写的就不用使用这句话。
    • JNIEXPORT
      JNI关键字,表示该方法可以被外部调用,不同的操作系统可能不同。
    • jstring
      表示该方法的返回值是java中的String类型,若java中返回值为int,则这里为jint,若没有返回值,这里为void。
    • JNICALL
      JNI关键字,可以没有,是约束函数入栈顺序,栈和堆内存清理规则的。
    • Java_com_example_jni_MainActivity_stringFromJNI
      Java语言哪个包下,哪个类的哪个方法。
    • JNIEnv
      c和java之间相互调用的桥梁,通过它相关的操作才能起作用。
    • jobject
      java中传递过来的对象,相当于Java中的this,这里表示MainActivity。
    • NewStringUTF
      c中的属性java也不能直接使用,所以需要单独的方法进行转换,这里是转换成java可以使用的字符串。

    C中修改Java的字段

    Java:

     private String name;
     
     // native方法,修改name字段
     public native void  updateName();
    
    • 1
    • 2
    • 3
    • 4

    native-lib.cpp中:

    extern "C"
    JNIEXPORT void JNICALL
    Java_com_example_jni_MainActivity_updateName(JNIEnv *env, jobject thiz) {
        // 拿到MainActivity
        jclass mainActivityCls = env->GetObjectClass(thiz);
        // 获取属性名称及类型
        jfieldID  name_fid = env->GetFieldID(mainActivityCls,"name", "Ljava/lang/String;");
        // 设置被修改的Java的属性的值
        jstring value = env->NewStringUTF("JNI测试");
        // 赋值
        env->SetObjectField(thiz,name_fid,value);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • jclass表示MainActivity.class
    • jfieldID表示name字段
    • “Ljava/lang/String;”表示name类型为String类型
    • jstring,表示创建字符串
    • SetObjectField,赋值
      整个流程走完和反射十分相似,一步一步将需要修改的内容取出来,然后进行赋值。

    调用Java方法

    Java:

        private int num01 = 1;
        private int num02 = 2;
        
        // java方法
        public int add(int num1,int num2){
            return num1 + num2;
        }
        // native方法
        public native int addNum();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    native-lib.cpp:

    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_example_jni_MainActivity_addNum(JNIEnv *env, jobject thiz) {
        // 拿到MainActivity.class
        jclass mainActivityCls = env->GetObjectClass(thiz);
        // 获取方法
        jmethodID add_mid = env->GetMethodID(mainActivityCls,"add", "(II)I");
        // 调用方法
        env->CallIntMethod(thiz,add_mid,100,200);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • GetMethodID,获取java方法。
    • “add”,需要调用的java方法的方法名。
    • “(II)I”,两个参数都为int,且返回值也为int。
    • CallIntMethod,调用java方法。
    • 100,200,调用java方法需要的参数。
  • 相关阅读:
    回忆初学编程的糗事:愚蠢的代码也是宝贵的学习经验
    什么?“裸辞”一个月拿到13家offer,网友:你是在找存在感吗···
    Golang学习记录:基础知识篇(一)
    如何让固定资产年终盘点更轻松?
    如何修改运行中的docker容器的端口映射
    Windows配置python3环境变量解决无法识别pip指令报错
    分布式ID之雪花算法
    如何在本地上次文件到GitHub
    《重构:改善既有代码的设计》读书笔记(上)
    如何建立一套完善的销售管理体系?
  • 原文地址:https://blog.csdn.net/qq_34202054/article/details/126349254