返回顶部
首页 > 资讯 > 后端开发 > JAVA >JNI详解
  • 180
分享到

JNI详解

java开发语言c++c语言 2023-09-02 21:09:56 180人浏览 薄情痞子
摘要

JNI简介 JNI全名java native interface,相当于java和C中间的桥梁作用,一种协议.通过JNI就可以让java调用C语言或者c++代码,并且可以让C调用java代码. 如下图所示  安卓系统的结构图如下: J

JNI简介

JNI全名java native interface,相当于java和C中间的桥梁作用,一种协议.通过JNI就可以让java调用C语言或者c++代码,并且可以让C调用java代码.

如下图所示

 安卓系统的结构图如下:

JNI_C的第一个程序

软件DEV

调用起java字节码,可以创建一个.java文件进行编译生成class文件,配置Class的环境变量,通过system(“java 文件名”)调用,此方法没有用到JNI.(了解) 

JNI_C语言的基本类型

回顾java的8大基本类型:

byte:占用1个字节

short:占用2个字节

int:占用4个字节

char:占用2个字节

float:占用4个字节

double:占用8个字节

boolean:占用1个字节

long:占用8个字节

C语言如下:

 输出为:

JNI_输出函数 

printf("你要输出的内容")

 %d - int

 %ld - long int

 %c - char

 %f - float

 %u - 无符号数

 %hd - 短整型

 %lf - double

 %x - 十六进制输出

 %0 - 八进制输出

 %s - 字符串

 输出结果:

JNI_输入函数 

Scanf(占位符,内存地址);

输出结果:

JNI_指针 

输出结果:

指针和指针变量的关系:

指针就是地址,地址就是指针

地址就是内存单元的编号

指针变量是存放地址的变量

指针和指针变量是两个不同的概念

但是要注意:通常我们叙述时会把指针变量简称为指针,但是它们的实际含义不一样

指针里面存放的100, 这个时候讲的指针是一个地址而且是一个具体的地址

指针里面存放的地址,  这个时候指针是一个指针变量是可变的(地址可能指向不同的东西)

为什么要用指针?

指针可以直接访问硬件比如显卡的绘图

(很多驱动居于C开发的原因就是C可以直接驱动硬件)

快速传递数据,指针表示地址

返回一个以上的值(返回一个数组或者结构体的指针)

表示复杂的数据结构(结构体)

方便处理字符串

指针有助于理解面向对象

*的三种含义

数学运算符:3*5

定义指针变量:int* p;

指针运算符(取值):*p(取p的地址在内存中的值)

 JNI_C互换两个数字

传统方法:

输出结果:

传送过去之后地址不相同导致了数值没有变化

正确方法如下:

输出结果如下成功转换值:

 JNI_函数返回一个以上的值

输出结果:

在main方法里面直接调用了close并没有返回值,但是返回了两个值,这两个值进行了改变,原因就是指针

通过被调函数修改主调函数普通变量的值

  1. 实参必须是普通变量的地址
  2. 形参必须是指针变量
  3. 被调函数中通过 *形参名的方式修改主调函数相关变量的值

JNI_多级指针

 输出结果:

解析: *p是存放地址对应的值.那么*address = &address3 也就是address3地址对应的值

**address4 就是 &&address3就是address3地址对应的值,也就是&address2

***address4就是&address1  ****address4就是 &i也就是100;    

JNI_数组

输出结果为:

*iArray + 0 :数组是一个连续的内存空间,内存空间就是地址.加上下一个下标就跳到了下一个

*(iArray + 0):加上坐标再去取相当于根据下标进行取值,这种是正规的做法

&iArray:数组的地址和数组的首地址相同

&iArray再数组中等同于iArray

JNI_输入数组

输出结果:

 JNI_静态内存分配

 输出结果:

 解析:静态内存是程序编译后系统(栈)分配的,由系统自动释放, 所以当第一个执行完之后对func()进行回收再去进行取值没了.

JNI_动态内存分配

 输出结果:

解析:temp = malloc(sizeif(int))返回的是一个长度为4的内存地址,相当于在堆区里面又申请了一块内存空间,存放的就是i的内存地址.当执行完func()后,fun()被回收,但是申请的还在,所以怎么取值都不会变.但是造成了资源没有回收.可以通过free(temp)回收掉手动申请的temp内存空间.

动态内存和静态内存

静态内存是程序编译执行后系统自动分配,由系统自动释放,静态内存是栈分配的

动态内存是开发者手动分配的,是堆分配的

  1. 从静态内存分配.内存在程序编译的时候已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量,
  2. 从栈上创建.在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限
  3. 从堆上分配,也称为动态 内存分配.程序运行时候用malloc或new申请任意多的内存,程序员自己负责在何时用free和delete进行释放.动态分配的生存期间由我们决定,使用非常灵活但是问题也多.

JNI_动态创建数组

输出结果:

 解析:Maclloc()动态分配内存空间,realloc是重新分配空间

JNI_函数指针

 输出结果:

JNI_C联合体

 输出结果:

 JNI_枚举

 输出结果:

JNI_结构体

别名:typeof

 输出结果:

 一般函数名特别长使用别名代替,这种情况下才会使用.平时使用较少

结构体

 输出结果:

 JNI_结构体指针

 输出结果

 JNI_Android Studio下的NDK环境配置

  1. 解压NDK的zip安装包到非中文目录
  2. 配置path:解压后的NDK根目录,复制解压后的根目录到环境变量下面的path中加入,然后执行ndk-build提示如下则配置成功

 JNI_快速开发

  1. 创建一个project,首先写JAVA代码,如下

2.创建JNI.java调用C的代码

3.在JAVA同级目录下创建jni目录

 

在jni目录下面写.C文件

注:String类型的返回值就是jstring其他的如下所示,后面的是包名类名前面加上Java,NewStringUTF可在jni.h(就在下载的ndk中)文件中查询:

项目目录(main)下面执行ndk-build,提示如下.发现需要创建Android.mk

 

在jni目录下面创建Android.mk如下图所示

再次执行ndk-build生成.so文件如下所示,目录有指向

 

查看生成的.so文件,前面带lib是正确的,之后引用.so文件进行JAVA调用C(要注意System引包是long)

提示dlopen failed: library "libhello.so" not found 没有找到这个.so文件,在build.gradle里面进行配置如下(与buildtypes同级):

 最终输出结果:

头信息可以简便生成.进入app/src/main目录下,执行:

javah -d jni -classpath ./java com.example.jnijianbian.MainActivity

如下,之后在android studio项目的jni目录下就能看见.h的头文件

 

 .mk文件信息如下

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := helloLOCAL_SRC_FILES := hello.cinclude $(BUILD_SHARED_LIBRARY)

 .gradle信息如下:

    sourceSets{        main{            jniLibs.srcDirs = ['src/main/libs','libs']        }    }

 JNI_开发中常见错误

一 本地方法没有找到

1.当java调用C的native方法名中有_,那么在C实现中也会有_就会很迷惑,不清楚是方法名还是类名

当重新执行ndk-build后,运行程序崩溃,提示Native method not found

解决:在C函数名里面前面加个1 比如 say_hello -> say_1hello,当然特多别的1时候可以通过javah生成方法名,自动生成一个名字.如果jdk1.7以上到项目的src目录下面运行javah  javah要生成C函数名字的java全路径(就是native方法的全路径)

2.忘记写System.loadLibrary()  可以通过静态代码块加载.so文件

二 找lib的时候(.so文件)返回null

1..so文件名字写错 lib前缀去掉, .so后缀去掉 剩下的就是要加载文件的名字

2.当前的.so文件不被cpu平台支持,需要通过在jni目录下添加Application.mk来指定编译后.so文件支持的cpu平台   例如 APP_ABI := armeabi x86

JNI_JAVA传递给C String类型数据

当执行javah -d jni -classpath ./java com.example.intdatasend.MainActivity时候会提示如下:

 解决办法:AndroidStudio右下角选择编码格式为GBK编码如下所示(或者用notepad++改变编码为ANSI)

C语言中是没有String类型的,所以需要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;}

 之后进行返回

JNI_java传递int类型数组数据给C

java中定义如下:

                int[] arr = new int[]{1, 2, 3};                int result[] = getIntArray(arr);                for (int i : result) {                    System.out.println("i =" + i);                }    public native int[] getIntArray(int[] arr);
//C返回如下    JNIEXPORT jintArray JNICALL Java_com_example_intdatasend_MainActivity_getIntArray      (JNIEnv * env, jobject thiz, jintArray jarr){      //获取数组的长度      int length = (*env)->GetArrayLength(env,jarr);      //获取数组首地址      int* p = (*env)->GetIntArrayElements(env,jarr,0);      int i;      for(i = 0; iSetIntArrayRegion(env,jarr,0,length,p);      return jarr;      }

输出结果:

JNI_向控制台输出日志

首先在jni目录下面的Android.mk添加一句

LOCAL_LDLIBS += -llog

之后再C的头文件添加如下

#include #define LOG_TAG "memorytester"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

再执行ndk-build就可以打印日志了,调用方法如下:

LOGD("length=%d",length);

输出结果:

JNI_反射回调JAVA方法

首先回顾Java的反射调用如下:

 JNI的调用:

首先定义Java调用C的方法以及C回调的方法如下:

public class JNI {    public void helloFromJava() {        System.out.println("我来自java");    }    public int add(int x, int y) {        return x + y;    }    public void printString(String s) {        System.out.println(s);    }    public native void callBackVoid();    public native void callBackInt();    public native void callBackString();}

方法签名的生成:

编译后cd到如下目录

 执行javap -s com/example/intdatasend/JNI (想声明方法签名的全类名)如下:

 之后在JNI目录下创建Android.mk进行编写,在grade里面设置路径,通过命令生成方法名,之后C代码实现如下

#include #include #include #include #define LOG_TAG "memorytester"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)JNIEXPORT void JNICALL Java_com_example_intdatasend_JNI_callBackVoid  (JNIEnv * env, jobject thiz){  //1.找到字节码  jclass clazz = (*env)->FindClass(env,"com/example/intdatasend/JNI");  //2.找到方法 ()V方法签名 void方法  jmethodID methodID = (*env)->GetMethodID(env,clazz,"helloFromJava","()V");  //3.创建对象(可选)  //4.通过对象调用方法  (*env)->CallVoidMethod(env,thiz,methodID);  }JNIEXPORT void JNICALL Java_com_example_intdatasend_JNI_callBackInt  (JNIEnv * env, jobject thiz){    //1.找到字节码    jclass clazz = (*env)->FindClass(env,"com/example/intdatasend/JNI");    //2.找到方法 (II)I表示两个int类型参数并且返回int    jmethodID methodID = (*env)->GetMethodID(env,clazz,"add","(II)I");    //4.通过对象调用方法    int result = (*env)->CallIntMethod(env,thiz,methodID,3,4);    LOGD("result=%d",result);  }JNIEXPORT void JNICALL Java_com_example_intdatasend_JNI_callBackString  (JNIEnv * env, jobject thiz){      //1.找到字节码      jclass clazz = (*env)->FindClass(env,"com/example/intdatasend/JNI");      //2.找到方法 (II)I表示两个int类型参数并且返回int      jmethodID methodID = (*env)->GetMethodID(env,clazz,"printString","(Ljava/lang/String;)V");      //4.通过对象调用方法      jstring jstr = (*env)->NewStringUTF(env,"hello");      (*env)->CallVoidMethod(env,thiz,methodID,jstr);  }

输出结果:

来源地址:https://blog.csdn.net/weixin_46987432/article/details/127707626

--结束END--

本文标题: JNI详解

本文链接: https://www.lsjlt.com/news/391517.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • JNI详解
    JNI简介 JNI全名java native interface,相当于java和C中间的桥梁作用,一种协议.通过JNI就可以让java调用C语言或者C++代码,并且可以让C调用java代码. 如下图所示  安卓系统的结构图如下: J...
    99+
    2023-09-02
    java 开发语言 c++ c语言
  • Android JNI详解
    目录 一、前言: 二、JNI简介 三、JNI函数注册 3.1静态注册: 3.2 动态注册 四、函数签名  4.1 什么是函数签名: 4.2 为什么需要函数的签名: 4.3 如何获取函数的签名 五、JNIEnv 5.1 何为JNIEnv: 5...
    99+
    2023-09-15
    android
  • 详解JNI到底是什么
    目录一、前言二、准备java代码三、生成头文件四、gcc环境安装五、生成动态链接库六、总结一、前言 首先回顾一下jni的主要功能,从jdk1.1开始jni标准就成为了java平台的一...
    99+
    2024-04-02
  • C语言JNI的动态注册详解
    目录总结JNI的静态注册就是Javah生成头文件,本章第一篇已经讲过,现在我们来讲讲第二种方式,JNI动态注册。首先是module的build.gradle: android {...
    99+
    2024-04-02
  • 较详细的JNI简介
    在Java中,有时候我们不得不要去使用其他语言的代码,比如说:你的应用需要访问系统的各个特性和设备,这些特性和设备通过java平台是无法访问的。你已经有了大量的测试过和调试过的用另一种语言编写的代码,并且知道如何将其导出到所有的目标平台上。...
    99+
    2023-05-31
    jni 简介 j
  • 实例详解Android中JNI的使用方法
    目录前言1.导入C语言的类2.接着导入Android.mk文件3.我们配置一下build.gradle文件4.好了,此时可以编译一下项目了6.将生成的so文件拷入src/main/j...
    99+
    2024-04-02
  • Android Studio 3.5版本JNI生成SO文件详解
    学习在于记录,把自己不懂得容易忘记得记录下,才是最好得选择。 废话不多说,想要在Android开发中嵌入c/c++代码,直接开始如下步骤 1、创建需要调用的Java类 在你某个指定的...
    99+
    2024-04-02
  • 基于Android studio3.6的JNI教程之opencv实例详解
    基本环境: Android studio3.6 NDK:r14b(尽量使用该版本) Opencv3.4.1 android sdk (1)新建工程OpenCVDemo,选择,...
    99+
    2022-06-06
    Android Studio studio jni opencv Android
  • Java中怎么理解JNI原理
    这篇文章主要讲解了“Java中怎么理解JNI原理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中怎么理解JNI原理”吧!实例:环境说明:ubuntu 10.4.2 LTS系统程序清单...
    99+
    2023-06-17
  • JNI开发示例
    小编给大家分享一下JNI开发示例,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!案例1:Native项目如果开发者们只是想做简单的Native开发,并非为第三方提供...
    99+
    2023-06-14
  • android studio JNI开发
    一、JNI的作用: 使Java与本地其他类型语言(C、C++)交互; 在Java代码调用C、C++等语言的代码 或者 C、C++调用Java代码。 由于JAVA具有跨平台的特点,所以JAVA与本地代码的交互能力弱,采用JNI特性可以增强JA...
    99+
    2023-09-03
    android studio android
  • 鸿蒙手机版JNI实战案例解析(JNI开发、SO库生成、SO库使用)
    目录鸿蒙JNI开发现状案例演示案例1:Native项目案例2:Native项目总结鸿蒙JNI开发现状 现阶段,不仅鸿蒙JNI相关的开发资料较少,而且Phone相关的JNI开发资料几乎...
    99+
    2024-04-02
  • 利用android studio 生成 JNI需要的动态库so文件 图文详解
    一、环境搭建 1.1 Android studio2021.2.1安装 到官网下载,此处不再陈述 1.2 JNI安装 JNI 是JDK里的内容,电脑上正确安装并配置JDK即可。 1.3 NDK安装 直接在Android studio下载(F...
    99+
    2023-09-12
    ndk-build cmake jni ndk so库文件
  • Android jni 线程同步
    文章目录概述问题示例c++层java层结果解决办法java层加锁c++层加锁java层和c++层共用一个锁 概述 android中可以通过jni...
    99+
    2022-06-06
    线程同步 jni 线程 Android
  • Android使用android studio配置并运行Jni详细流程
    前言: 本文章以Android Studio为IDE,以手动编译不使用cmake的方式为例,在某个已有普通android项目基础上,创建jni程序并运行。详细介绍以下内容: 1、环境配置(Android Studio、SDK、JDK、NDK...
    99+
    2023-10-06
    android android studio gradle
  • Android JNI开发提高篇
      有关JNI的开发技术,我们继续围绕Android平台进行,JNI可以支持C或C++,从目前为止我们写过的JNI代码均为C实现的,即文件名为.C而C++的和这些有什么不同...
    99+
    2022-06-06
    jni Android
  • java中jni有什么用
    java中jni的作用是:jni提供的API允许java和其他语言进行交互,因此主要是使用jni来调用c或c++中的代码。jni含义:jni的主要功能是从jdk1.1开始jni标准就成为了java平台的一部分,它提供的一系列的API允许ja...
    99+
    2024-04-02
  • Android中JNI如何使用
    Android中JNI如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1.导入C语言的类首先我们需要把C语言写的功能类放入我们的项目中。这里我直接从资料中...
    99+
    2023-06-20
  • Android JNI打印logcat日志
    在 JNI 中打印日志可以使用 __android_log_print 函数来实现。该函数是 Android NDK 提供的一个用于在本地代码中输出日志消息到 logcat 的方法。 要在 JNI 中...
    99+
    2023-10-21
    android JNI
  • JNI语言基本知识
    JNI简介JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代...
    99+
    2023-05-30
    jni 基础知识 j
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作