Android JNI调用流程

news2024/11/24 10:29:21

文章目录

  • 前言
  • 一、JNI是什么
  • 二、JNI的优劣
  • 三、JNI的开发流程
    • Java调用C++函数
      • 1、创建声明native方法的Java工程,加载native函数的动态库,生成.h文件
      • 2、创建实现C函数的C工程,将本地代码编译成动态库
        • C函数和Java本地方法的隐式映射(相对简单)
        • C函数和Java本地方法的显式映射
      • 3、加载.dll文件,运行java工程
    • C++调用Java方法

前言

对基本的JNI开发流程予以记录

参考书籍:《JNI_NDK开发指南》
参考博客:
JNI开发流程——东邪丶
JNI开发流程——Android百晓生

一、JNI是什么

JNI全称为Java Native Interface,主要用于实现Java和C/C++的通信。

在这里插入图片描述

二、JNI的优劣

优势:

  • 能够访问一些底层/系统级接口(一般这类接口都是C/C++编写的)
  • 能够直接调用C/C++,一定程度上能够提升执行效率
  • 能够避开一些java层的限制,比如JVM的内存开销过大等等
  • 确保代码在不同的平台上方便移植

劣势:

  • 程序可靠性会降低
  • 在C/C++中通过JNI访问Java的对象方法、对象属性时,相比于Java中自行调用,效率不高

三、JNI的开发流程

Java调用C++函数

1、创建声明native方法的Java工程,加载native函数的动态库,生成.h文件

  • Java中native方法的声明方式:public static native xxx(xxx);
  • Java中加载native动态库的方式:System.LoadLibrary(xxx)或System.load(xxx);
public class JniTest {

    // 获取字符串
    public static native String getStringFromC();
    // 获取相加值
    public static native int addFromC(int a, int b);

    static {
        // 需要在System.getProperty("java.library.path")路径下放入对应的jni_test.dll包
        // System.loadLibrary("jni_test");
        // 需要使用绝对路径,且需要添加后缀
        System.load("C:/Users/kqli/IdeaProjects/shanguigu_interview_java/src/com/kqli/jni/jni_test.dll");
    }

    public static void main(String[] args) {
        // 输出java library路径
        // System.out.println(System.getProperty("java.library.path"));
        String stringFromC = getStringFromC();
        System.out.println();
        System.out.println(stringFromC);
        int a = 1, b = 1;
        System.out.println(String.format("%d + %d = %d", a, b, addFromC(a, b)));
    }
}

Java加载native动态库,有两种API可实现:

①System.loadLibrary(“LibraryName”);
该API只需要指定动态库名字即可,不需要加前后缀。
且java会到java.library.path系统属性指定的目录下查找动态库文件,如果没有找到会抛出java.lang.UnsatisfiedLinkError异常。
若.dll文件有多个工程使用,可放到一个统一目录,在Path环境变量中配置存放路径,系统也能正确加载

②System.load(“/Users/Desktop/LibraryName.so”);
该API需要指定动态库的绝对路径名,且要加上前缀和后缀。

Java静态代码块中加载动态库,防止在未加载动态库之前就调用native方法

Java在创建类实例时,类会先被ClassLoader先加载到Java VM中,紧接着调用类的static静态代码块,所以在此时加载动态库可有效避免native方法调用比加载动态库时更早。

使用命令生成java工程.h文件

javac -h [目标文件路径] [文件名.java]

如出现编码格式异常则使用

javac -encoding utf-8 -h [目标文件路径] [文件名.java]

执行完成,会在src目录下生成.h头文件,如下图:

在这里插入图片描述在这里插入图片描述

2、创建实现C函数的C工程,将本地代码编译成动态库

1)将.h头文件拷贝到c工程,并添加关联到项目(此时.h中#include<jni.h>头文件引入报错)
2)JDK中搜索jni.h,将jni.h和jni_md.h文件复制到C工程中,并修改引用为#include “jni.h”(解决jni.h头文件报错)

注:#include指令使用区别,系统头文件引入使用<>;第三方头文件引入使用" ",所以此处需修改引用为#include “jni.h”

3)创建c01.c、c02.c文件,实现C函数

Java调用C函数需要做C函数和Java本地方法的映射,建立该映射有两种方式: 显式映射和隐式映射。

C函数和Java本地方法的隐式映射(相对简单)

在c01.c中实现Java与C函数隐式映射

#include "com_kqli_jni_JniTest.h"
#include "jni.h"

//函数实现
JNIEXPORT jstring JNICALL Java_com_kqli_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jclass) {
    return (*env)->NewStringUTF(env, "Hello, kqli!");
}

JNIEXPORT jint JNICALL Java_com_kqli_jni_JniTest_addFromC(JNIEnv *env, jclass jclass, jint a, jint b)
{
    return a + b;
}
C函数和Java本地方法的显式映射

在c02.c中实现Java与C函数显式映射

#include "jni.h"

#define ARRAY_SIZE(arr)   (sizeof(arr) / sizeof((arr)[0]))

// C函数需要比Java本地方法多出两个参数,这两个参数之后的参数列表与Java本地方法保持一致
// 第一个参数表示JNI环境,该环境封装了所有JNI的操作函数
// 第二个参数为Java代码中调用该C函数的对象
// jint表示JNI的int类型,在本文后面会给出所有JNI类型
jint add(JNIEnv *env, jobject thiz, jint a, jint b)
{
    return a + b;
}

jstring getString(JNIEnv *env, jobject thiz)
{
    return (*env)->NewStringUTF(env, "hello, kqli!");
}

static const JNINativeMethod methods[] = {
    // 第一个参数为Java本地方法名
    // 第二个参数为函数签名:(参数签名)返回值签名, 在本文后面会给出所有签名符号
    // 第三个参数为C函数
    {"addFromC", "(II)I", (void *)add},   // 建立Java本地方法和C函数的映射
    {"getStringFromC", "()Ljava/lang/String;", (void *)getString},
};

// 在Java中调用System.loadLibrary方法时会调用到该函数
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
    JNIEnv *env;
    jclass cls;

    // 获取JNI环境
    if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_8)) {
        return JNI_ERR;
    }

    // 获取Java类
    // JNI_OnLoad函数写法基本固定, 唯一需要修改的是FindClass的第二个参数,即类名
    cls = (*env)->FindClass(env, "com/kqli/jni/JniTest");
    if (cls == NULL) {
        return JNI_ERR;
    }

    // 注册本地方法
    if ((*env)->RegisterNatives(env, cls, methods, ARRAY_SIZE(methods)) < 0)
        return JNI_ERR;

    return JNI_VERSION_10;
}

点击查看语法解释

动态库共有以下几种:

  • Windows:.dll库
  • Linux/Unix:.so库
  • Mac os x:.jnilib库

Windows编译.dll库

输出.o文件:gcc -c [.c文件名] -o [输出的.o文件]
输出.dll库:gcc [.o文件] -o [输出的.dll文件] -shared

在这里插入图片描述

3、加载.dll文件,运行java工程

将vs中生成的.dll动态库拷贝到java工程目录下,运行java工程

使用System.loadLibrary(“jni_test”)时,.dll文件须放到工程根目录下系统动态加载时才能找到,否则会报路径错误;
使用System.load(“C:/Users/kqli/IdeaProjects/shanguigu_interview_java/src/com/kqli/jni/jni_test.dll”)时,.dll文件必须与填写路径一致

运行成功!
在这里插入图片描述

C++调用Java方法

待更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1091927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

压缩炸弹,Java怎么防止

一、什么是压缩炸弹&#xff0c;会有什么危害 1.1 什么是压缩炸弹 压缩炸弹(ZIP)&#xff1a;一个压缩包只有几十KB&#xff0c;但是解压缩后有几十GB&#xff0c;甚至可以去到几百TB&#xff0c;直接撑爆硬盘&#xff0c;或者是在解压过程中CPU飙到100%造成服务器宕机。虽然…

11-网络篇-DNS步骤

1.URL URL就是我们常说的网址 https://www.baidu.com/?from1086k https是协议 m.baidu.com是服务器域名 ?from1086k是路径 2.域名 比如https://www.baidu.com 顶级域名.com 二级域名baidu 三级域名www 3.域名解析DNS DNS就是将域名转换成IP的过程 根域名服务器&#xff1a…

python2 paramiko 各种报错解决方案

一、介绍 paramiko是一个基于SSHv2协议的python库&#xff0c;支持以加密和认证的方式进行远程服务器的连接&#xff0c;用于实现远程文件的上传、下载或通过ssh远程执行命令。 paramiko支持Python&#xff08;2.7&#xff0c;3.4&#xff09;版本 paramiko库可直接使用pip …

谈谈C++中模板分离式编译出现的一些问题

什么是分离式编译 通俗的来讲就是将声明和定义分离在不同文件中 一个程序由若干个源文件共同实现&#xff0c;而每个源文件单独编译生成目标文件&#xff0c;最后将所有 目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。 正常函数与模板分离式编译 看代码&…

生物制剂\化工\化妆品等质检损耗、制造误差处理作业流程图(ODOO15/16)

生物制剂、化工、化妆品等行业&#xff0c;因为产品为液体&#xff0c;产品形态和质量容易在各个业务环节发生变化&#xff0c;常常导致实物和账面数据不一致&#xff0c;如果企业业务流程不清晰&#xff0c;会导致系统大量的库存差异&#xff0c;以及财务难以核算的问题&#…

上门服务小程序源码 理疗,足疗,美容SAP上门服务小程序源码

上门服务小程序源码 理疗&#xff0c;足疗&#xff0c;美容SAP上门服务小程序源码 运行环境&#xff1a;Nginx 1.20PHP7.1MySQL 5.6 通过HBuilder X编译小程序APP版本 一、上门预定操作 1、技师管理。 技师满意度进行统一跟踪评估&#xff0c;进行分级管理&#xff0c;分级…

Web测试框架SeleniumBase

首先&#xff0c;SeleniumBase支持 pip安装&#xff1a; > pip install seleniumbase它依赖的库比较多&#xff0c;包括pytest、nose这些第三方单元测试框架&#xff0c;是为更方便的运行测试用例&#xff0c;因为这两个测试框架是支持unittest测试用例的执行的。 Seleniu…

Canal安装

安装和配置Canal Canal Framework 是阿里巴巴开源的一款基于数据库增量日志解析和同步的数据中间件。它主要用于解决分布式系统中数据同步的问题&#xff0c;支持多种数据源&#xff0c;如 MySQL、SQL Server、PostgreSQL、Oracle 等&#xff0c;同时也支持多种数据目标&#…

函数栈帧的创建与销毁(保姆级讲解)

局部变量是怎么创建的? 在为main函数开辟栈帧空间时&#xff0c;在一定范围内初始化成0CCCCC&#xff0c;再把里面0CCCC的一些开辟空间给局部变量使用。 为什么局部变量的值是随机值? 因为我们在为main函数开辟栈帧空间时&#xff0c;会将一定范围内空间初始成0CCCCCC里面…

【宏实现二进制奇偶位交换】

文章目录 一. 二进制奇偶位交换说明意思&#xff1f;二. 解题思路三. 代码验证四. 总结 一. 二进制奇偶位交换说明意思&#xff1f; 就是一个int类型的整数在操作系统下是32位二进制01序列&#xff0c;第一位和第二位交换&#xff0c;第二位和第三位交换&#xff0c;依次类推。…

口袋参谋:生意参谋指数转换工具,比对手更了解对手!

​所谓“知己知彼&#xff0c;百战不殆”&#xff0c;比对手更了解对手&#xff0c;就是提升自己的好机会。 在竞争如此激烈的淘宝天猫上&#xff0c;淘宝平台为了保护商家店铺数据&#xff0c;将真实数值全部隐藏&#xff0c;变成了指数。 所以我们查看市场排行、市场大盘、竟…

awvs安装教程和使用

awvs安装和使用 文章目录 awvs安装和使用安装&#xff08;awvs&#xff09;Acunetix-v23.9-Windows1 安装前准备1.1 查看帮助文档&#xff0c;修改此文件1.2 追加hosts 参数1.3 根据REANME.txt帮助文档提示&#xff0c;启动工具(Now install the tool)&#xff0c;双击 1.4 安装…

(vue3)大事记管理系统 文章管理页

[element-plus进阶] 文章列表渲染&#xff08;带搜索&到分页&#xff09; 表单架设&#xff1a;当前el-form标签配置一个inline属性&#xff0c;里面的元素就会在一行显示了 中英国际化处理&#xff1a;App.vue中el-config-provider标签包裹组件&#xff0c;意味着整个组…

Cesium 问题:加载 geojson 文件后使用 remove 方法移除,但浏览器内存会持续增长并为得到释放直到浏览器崩掉

文章目录 需求分析解决需求 在开发中,加载了 geojson 文件后,浏览器内存会增长,当使用remove方法移除后再次添加,浏览器内存持续增长并未减少,直到第三次交互添加,浏览器崩掉了 分析 在处理大量实体或长时间运行的应用程序中,及时释放不需要的实体是一种良好的做…

数据结构 - 3(链表12000字详解)

一&#xff1a;LinkedList的使用 1.1 ArrayList的缺陷 上篇文章我们已经基本熟悉了ArrayList的使用&#xff0c;并且进行了简单模拟实现。由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&am…

Java设计模式:Callback

介绍 回调&#xff08;Callback&#xff09;是一种设计模式&#xff0c;在这种模式中&#xff0c;一个可执行的代码被作为参数传递给其他代码&#xff0c;接收方的代码可以在适当的时候调用它。 在真实世界的例子中&#xff0c;当我们需要在任务完成时被通知时&#xff0c;我…

【Linux】从零开始学习Linux基本指令(一)

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;Linux入门 &#x1f525;该文章主要了解Linux操作系统下的基本指令。 目录&#xff1a; ⌛️指令的理解⏳目录和文件的理解⏳一些常见指令✉…

C++入门篇11 模板进阶

一、非类型模板参数 模板参数分为类型形参和非类型形参 类型形参&#xff1a;出现在模板参数列表里&#xff0c;跟在class/typename之后的参数类型名称非类型参数&#xff1a;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将参数当作常量来使用 …

SRAM电路设计

RAM是随机存取存储器&#xff08;random access memory&#xff09;&#xff0c;是计算机内部存储器中的一种&#xff0c;也是其中最重要的&#xff0c;计算机和手机中一般把其叫做&#xff08;运行&#xff09;内存&#xff0c;它的速度要比硬盘快得多&#xff0c;所以用运行程…

设计师都应该知道的事:极简主义家具该怎么去用

这座房子有黑暗而沉重的特征&#xff0c;包括棕色和白色的马赛克浴室瓷砖&#xff0c;弯曲的锻铁壁灯和土黄色的威尼斯石膏墙。但由于房屋与他们的风格相去甚远&#xff0c;白色&#xff0c;干净和简约&#xff0c;接下来我们就着这个方向去帮助房主进行改造。 她解释说&#x…