【Android JNI 开发入门指南】

Android JNI 入门(含完整Demo) //自动检测 MainActivity 生成宏 #define com_example_jni_MainActivity_A 234L //函数的声明 extern “C” JNIEXPORT jstring JNICALL Java_com_example_jni_MainActivity_getString (JNIEnv *, jobject); #ifdef __cplusplus//如果是 C++ 什么事情都不干 } #endif #endif 这里注释添加的非常清楚,就不在啰嗦了,如若不懂,评论 阅读详情
AI摘要

本文详细介绍Java Native Interface (JNI) 的概念、用途及开发流程,包括如何使用NDK进行交叉编译,实现Java与C/C++代码的交互,以及常见错误解决方法。

Android JNI 开发入门指南

1. 【JNI介绍】

  • What (是什么): JNI (JAVA Native Interface)java本地接口。

    • 通过JNI可以实现Java和本地代码(C/C++)之间相互调用。

    • JNI可以看做是翻译官,实际上就是一套协议。

    • 因此类似于AIDL(安卓接口定义语言),aidl是用于进程间通信的一套协议(接口)。

  • Why (为什么要用):

    • 由于java的跨平台能力,导致了一些弊端,运行在虚拟机上,无法真正直接的操作系统硬件。

    • JNI则可以扩展JAVA的能力,让java也能通过它来操作硬件驱动。(智能家居,物联网…)

    • java是解释型语言,运行效率相对较低,而C/C++的效率要高很多,通过JNI可以把耗时操作放在C/C++中实现可以提高Java程序的运行效率。

    • java代码编译的字节码文件安全性较差, 可以通过jni 把重要的敏感的业务逻辑放到c/c++去实现,由于c/c++反编译比较困难,因此安全性较高。

    • 代码移植,复用已经存在的c代码(opencv,ffmpeg…)

    • C历史悠久,通过JNI可以调用优秀的C开源类库。

  • How (怎么用):

    • 熟练Java
    • C/C++ 能看懂 会调用。
    • JNI开发流程 NDK native develop kit

2. 【NDK(JNI)开发介绍】

  1. [交叉编译]

    • 在一个平台上去编译另一个平台上可以执行的本地代码。

    • 交叉编译的实现原理: 模拟不同平台的特性去编译代码。

    • Android系统运行的 CPU 架构 :ARM,X86,mips

  2. [NDK (native Develop Kit),谷歌提供的: 用作JNI开发的交叉编译工具集。]

    △安装介绍;如果电脑与JDK都是32位的则选择 android-ndk-r9d-windows-x86 的开发工具包。

     					   64为的则选择 android-ndk-r9d-windows-x86_64 的开发工具包。
    

    △使用介绍:解压就能用

    △NDK解压后的:常用目录,文件介绍:

     * docs: 帮助文档
    
     * platforms : 平台版本文件夹,使用时选择项目支持的最小版本号所对应的文件夹,每一个版本号的文件夹中包含了不同cpu架构的资源文件
    
     		[例如]:我的安卓项目设置的最低支持SDK版本为 api-14,则应该选择使用: platforms/android-14下的头文件,与.so文件。
    
     * include : 存储jni开发中常用的 .h头文件
     * lib : 	 google打包好的 提供给开发者使用的:.so文件,.o文件。
     * samples : google官方提供的样例工程 可以参考进行开发
     * android-ndk-r9d\build\tools : linux系统下的批处理文件 在交叉编译时会自动调用
    
     * ndk-build.cmd :交叉编译的命令。(执行它就可以吧c代表编译成安卓应用可以支持的 .so文件)
     * cdt :      eclipse的插件 高亮C代码 C的代码提示。(一般在eclipse中会自带此插件)
    
  3. 【[JNI-Hello World】

    △需求:在Android程序中点击一个Button,实现调用C语言的一个函数,该函数返回一个字符串(Hello World)!

    △核心原理:在java中声明一个native方法,然后在C语言代码中实现这个native方法,然后在java代码中调用这个方法。

    ☆实现步骤: —可以参考:android-ndk-r9d-windows-x86.zip\android-ndk-r9d\samples\hello-jni 这个示例工程。

    • 简要步骤如下:

      1. 创建一个android工程
      2. java代码中写声明native方法
      3. 创建jni目录,编写c代码,方法名字要对应(有一定的命名格式)。
      4. 编写Android.mk文件,如果需要还需Application.mk
      5. NDK编译生成动态链接库
      6. java代码load动态库.调用native代码
    • [详细步骤如下]:

      • [1]:首先写java代码, 声明本地方法 用到native关键字 本地方法不用去实现。

          例:
          	/**
          	 *1. 声明一个java本地方法,利用native关键字,不需要方法体。
          	 */
          	public native String sayHelloFromC();	
        
      • 2:然后在安卓项目根目录下创建jni文件夹, 在jni文件夹下创建 hello.c文件。需要包含进两个头文件:include<stdio.h> 与 include<jni.h>

        △在hello.c中:创建本地函数,本地函数命名规则为: Java_包名_类名_本地方法名

        △按照JNI本地函数命名规则,定义我们自己需要的函数。函数的返回值类型可以参照JNI协议。


        在jni.h头文件中:
        typedef const struct JniNativeInterface* JNIENV ;//因此JNIENV 类型,实际上就是JniNativeInterface这个结构体的指针的别名。

        • 因此JNIEnv 是JniNativeInterface这个结构体的一级指针

        • 参数:JNIENV* env 就是结构体JniNativeInterface这个结构体的二级指针(因为它接收的就是一级指针的地址)

        • JniNativeInterface 这个结构体定义了大量的函数指针,因此就可以通过结构体指针来访问结构体中定义的函数指针所指向的函数了。

        • (env)-> 即可调用结构体中的函数指针。(因为env为二级指针,因此需要加,取出一级指针,然后对一级指针使用 -> )

        • 第二个参数jobject thiz: 调用本地函数的java对象就是这个jobject,在本例中也就是MainActivity.this

        △:注意:我们调用的所有的JNI本地代码函数,都需要带这两个参数。

          例:hello.c 如下:
        
          	#include<stdio.h>
          	#include<jni.h>
        
          	//由于程序的入口是MainActivity,因此无需再此处创建 main()函数
        
          	/**
          	 * 添加一个C语言本地函数:命名规则为: Java_包名_类名_本地方法名 。(本地方法名就是调用这个c函数的java native 方法名)
          	 */
          	jstring Java_com_hzy_jnihelloworld_MainActivity_sayHelloFromC(JNIEnv* env,jobject thiz){
          		//定义一个C语言的字符串
          		char* hello_str = "helloWorld!";
          		//通过:结构体(JniNativeInterface)的二级指针 env 调用结构体中声明的函数指针所对应的函数
          																		(jstring  NewStringUTF(JNIEnv*, const char*);)
          		return (*env)->NewStringUTF(env,hello_str);
          	}
        
      • [3]:在项目的 jni目录下 创建Android.mk (makefile) 告诉编译器.c的源文件在什么地方,要生成的编译对象的名字是什么

         例:Android.mk 如下:
        
         		LOCAL_PATH := $(call my-dir)
        
         	    include $(CLEAR_VARS)
        
         	   	LOCAL_MODULE    := hello   #指定了生成的动态链接库的名字
         	   	LOCAL_SRC_FILES := hello.c #指定了C的源文件叫什么名字
        
         	    include $(BUILD_SHARED_LIBRARY)
        
      • 4:用cmd进入项目的根目录,然后调用命令 ndk-build 编译c代码,编译成功生成的动态链接库.so文件,
        该文件的位置 lib->armeabi->libhello.so 。(右键项目–>refresh即可看到该文件出现。)

        注意:命令使用前需要将 ndk 解压后的目录,添加到系统的path环境变量中。

           如果运行的设备平台为x86,则需新建一个 Application.mk文件,在里面进行配置,实现让ndk编译出多个平台的so文件。--提高兼容性
        
      • [5]:在java代码中加载动态链接库 System.loadlibrary(“动态链接库的名字”); 也就是 jni/Android.mk 中 LOCAL_MODULE所指定的名字。

         //使用前需要加载编译成功后生成的,动态链接库文件
         	static{
         		System.loadLibrary("hello");
         	}
        
      •  例:
         	//调用本地方法
         	String result = sayHelloFromC(); //实际上会调用 .so文件中所定义的对应的本地代码函数。
         	Toast.makeText(getApplicationContext(), result, 0).show();		
        

3. 【jni开发中的常见错误 】

  • △ [java.lang.UnsatisfiedLinkError: Native method not found: 本地方法没有找到 ]

    • 本地函数名写错
    • 忘记加载.so文件 没有调用System.loadlibrary(); //一般在静态代码块中加载
  • △[findLibrary returned null ]

    • System.loadLibrary(“hello”); //注意:加载动态链接库时,name为 :jni/Android.mk 中 LOCAL_MODULE所指定的名字。

    • 平台类型错误:例如:把只支持arm平台的.so文件部署到了 x86cpu的设备上。 —因为默认情况下会生成arm平台的so文件。

      解决方案:

        * 在jni目录下创建 Application.mk 文件,并在里面指定: 
      
        	  APP_ABI := x86 			#则只支持x86平台
        	  APP_ABI := armeabi x86   #则只支持arm平台与x86平台
        	  APP_ABI := all 		  #全部支持
      
          	  APP_PLATFORM := android-14  #此处配置成自己项目所支持的最低的AndroidSDK版本,交叉编译时就不会 Warning!
      
          完成配置后,cmd进入项目根目录,然后ndk-build重新编译。即可!	  
      
  • △[用javah 命令:来生成头文件,解决自己写本地代码函数时,函数名格式写错的问题。]

    • 如果是jdk 1.7以上,则到项目 src目录下运行 javah命令

    • 如果是jdk 1.6,则到项目的bin目录下 classes文件夹下运行 javah命令

    • 写法:javah (native方法声明的java类的全类名)

      例:首先在mainActivity中编写一个native方法:public native String sayHelloFromC(); //native方法的方法体位于本地代码中

        然后到cmd中找到项目的...\src> javah com.hzy.jnihelloworld.MainActivity,
      
        	就会将该类中所有的 native方法,生成对应的本地代码函数到头文件中。
      
        刷新项目即可看到:src/com_hzy_jnihelloworld_MainActivity.h 
      
        	:方法名就生成成功了。
      
        		/*
        		 * Class:     com_hzy_jnihelloworld_MainActivity
        		 * Method:    sayHelloFromC
        		 * Signature: ()Ljava/lang/String;
        		 */
        		JNIEXPORT jstring JNICALL Java_com_hzy_jnihelloworld_MainActivity_sayHelloFromC(JNIEnv *, jobject);
      
  • △:解决CDT插件报错的问题(在.c文件中使用 JNIEnv, jstring这些关键字时,发现报错!)

    右键单击项目选择 properties :

    选中:c/c++ general->paths and symbols->include选项卡下->点击add…->file system
    选择ndk目录下 的platforms文件夹 对应平台下(项目支持的最小版本)usr 目录下 arch-arm -> include
    (apply)确定后 会解决代码提示和报错的问题。

4. 【JNI简便开发流程】

  • 前言:一个标准的JNI程序开发步骤就如上文所示,但是过于繁琐,因此安卓也提供了简便的开发流程。

  • [实现步骤]:

    • [1]:【首先编写java代码, 用native 声明本地方法。】

    • 2:【添加本地支持 右键单击项目->andorid tools->add native surport,然后指定生成的so文件的名字即可。】

        * 如果发现 finish不能点击,则需要给工作空间指定 ndk目录的位置(工具包解压后的存放位置)。
      
        * 进入:window->preferences->左侧选择android->ndk ,然后把ndk解压后的存放目录指定进来即可。
      
          △如果发现自己的eclipse中没有andorid tools->add native surport,这个选项则代表自己的ADT版本问题,
      
          		此时可以将一个ndk插件安装到eclipse中,重启eclipse就会发现有 andorid tools->add native surport这个选项了。
        	
        			例:com.android.ide.eclipse.ndk_23.0.2.1259578.jar,放置到eclipse的plugins目录中即可。
      

      △使用这种方式就无需手动创建 jni文

简述JNI开发 JNI全称为Java Native Interface,主要用于实现Java和C/C++的通信。 阅读详情

相关推荐

Android使用android studio配置并运行Jni详细流程

以手动不使用cmake方式,配置并运行jni项目的详细流程。结构清晰,按照步骤做,一定能够成功运行,一起来试试吧

yxw331291779的博客 7289

安卓NDK开发入门:从JNI原理到实战性能优化指南

在移动应用开发中,性能优化和硬件能力调用是核心技术挑战。通过原生开发技术,开发者可以突破高级语言虚拟机的限制,直接操作内存和CPU指令,实现极致性能。其核心原理在于建立Java/Kotlin与C/C++代码之间的高效通信桥梁,这通常通过JNI(Java Native Interface)实现。这项技术的核心价值在于处理计算密集型任务,如图像处理、音视频编解码和物理模拟,并能复用大量成熟的C/C++生态库。在安卓平台上,NDK(Native Development Kit)提供了完整的工具链来实现这一过程,涉

chubaisheng8627的博客 379

Android Studio JNI开发指南:从入门到精通!

JNI(Java Native Interface)是一种用于在Java和本地代码之间进行交互的技术。它为开发人员提供了使用C/C++编写高性能本地代码的能力,并将其与Java代码无缝集成。本文将介绍如何在Android Studio中进行JNI开发,帮助您快速入门并逐步精通这一技术。

CodeJolt的博客 448

Android Studio NDK 开发指南:JNI 入门教程

通过以上步骤,我们成功地创建了一个简单的 Android Studio NDK 项目,并实现了 JNI 的入门示例。在本篇文章中,我们将介绍如何使用 Android Studio 进行 NDK 开发,并通过一个简单的示例来演示 JNI 的基本用法。我们可以尝试编译和运行项目,查看我们的 JNI 函数是否能够正常工作并将字符串显示在 UI 上。文件,我们需要在该文件中调用刚刚编写的 JNI 函数,并将其返回值显示在 UI 上。文件,我们可以开始编写我们的 JNI 函数。加载了我们在 JNI 中定义的库。

PixelDude的博客 281

Android JNI开发入门实战指南

JNI(Java Native Interface)是Java平台提供的一种标准接口,允许Java代码与C/C++等本地语言进行高效交互。在Android开发中,JNI不仅为计算密集型任务(如音视频编解码、图像处理)提供了性能优化路径,还支持复用成熟的原生库(如FFmpeg、OpenCV),并可直接访问系统底层资源或硬件接口。其核心机制基于JNIEnv指针和jobject实例,实现跨语言函数调用与数据传递。通过。

weixin_35748962的博客 1035

Android Studio中NDK与JNI开发入门指南

Java Native Interface (JNI) 是一个编程框架,允许Java代码和其他语言写的代码(主要是C和C++)进行交互。其定义了一个标准的编程接口,使得Java虚拟机(JVM)中的Java代码能够调用本地应用程序接口(API)中的本地方法(用C、C++或其他语言实现的方法),以及反过来实现本地方法调用Java对象和其方法。JNI不仅提供了一种机制来调用本地方法,同时也需要一种方式来处理不同数据类型之间的转换。

weixin_32099703的博客 1310

Klocwork安装

简单介绍一下Klocwork在windows下的安装,操作系统是win7,Klocwork的版本是10.0。 第一步:由于Klocwork的安装程序已十分成熟,所以在安装之前不需要什么准备共走。双击Klocwork的安装包,会出现下图的安装引导界面,点击Next按钮。 第二步:下图是协议界面,点击Next按钮。 第三步:选择Klocwork的安装目录,点击Next按钮 第四步:

旮旯 9931

ROS2_IGH方案配置(二)——Ubuntu22.04 实时内核性能验证与调优

本文详细介绍了在Ubuntu22.04系统上配置ROS2_IGH方案时,如何验证和调优PREEMPT_RT实时内核性能。通过使用cyclictest工具进行基准测试和压力测试,分析中断与硬件延迟,并优化内核参数,确保系统满足工业控制领域对硬实时性的苛刻要求。文章还提供了针对ROS2的特殊优化建议和性能监控方案,帮助开发者实现毫秒级延迟的稳定控制。

weixin_30791095的博客 224

Windows 11 的C盘User(用户)文件夹下的用户文件夹名称的修改

背景介绍:由于系统重装导致Windows 11的系统用户名与C盘User(用户)文件夹下的用户名文件夹(公用文件夹旁边的文件夹)出现名称不一致,事例中系统用户名命名为“寂萧”,User(用户)文件夹下的文件夹名称却是“77555”。【QQ邮箱前五位真的真的看着很别扭】注:改名有风险,三思而后行。User(用户)文件夹下的用户名是中文在某些软件的应用场景下产生不便,导致很多软件莫名其妙报错,甚至无法使用,笔者不建议修改为中文名。//和笔者一样的强迫症除外//

2302_79084172的博客 10万+

【点云可视化】自制ply文件并使用open3d可视化点云

使用open3d可视化点云,需要将点云制作为ply文件传入函数中。 安装open3d 直接使用pip安装 pip install open3d ply文件 ply文件的结构为:文件头+数据内容,文件头从 ply 一行开始,到 end_header 为止,里面定义了此 ply文件中数据的组织形式,包括数据类型和个数。后面是点云的坐标信息及每个点的颜色属性的,参考自 如下为具有5个点的点云,end_header后每一行表示一个点的x,y,z,R,G,B信息 ply format ascii 1.0 comme

这个人很蓝 1万+

GEE:大面积研究区域数据分块导出,以解决内存限制和导出失败的问题

本文介绍了在Google Earth Engine (GEE)平台分块导出大面积研究区域数据的方法。针对直接导出大区域影像易失败的问题,作者提出通过网格分块技术进行处理。文章展示了完整的JavaScript代码示例,包含定义研究区域、加载ESA WorldCover数据集、设置分块参数(包括像素大小、分块尺寸)、创建网格系统、处理坐标转换等关键步骤。代码中还提供了错误处理和导出按钮功能,确保数据能可靠地分块导出到Google Drive。该方法有效解决了大区域数据导出时的内存限制问题,适用于全球范围的研究。

养乐多的博客 1186

STM32入门教程:智能马桶盖控制

通过编写相应的代码,您可以根据需要实现其他功能,例如座圈温度调节和液晶显示屏显示等。初始化配置 首先,使用STM32CubeMX创建一个新的工程,并选择您所使用的STM32单片机型号。智能马桶盖是一种集成了多种功能的智能家居设备,通过控制芯片来实现自动冲水、座圈加热、座圈温度调节等功能。在本教程中,我们将使用STM32系列单片机来控制智能马桶盖的各个功能。以上代码演示了如何通过STM32的GPIO控制引脚来控制智能马桶盖的冲水和座圈加热功能。写代码 接下来,我们将通过编写代码来控制智能马桶盖的各个功能。

2401_85258012的博客 657

2.Vivado软件基础操作

在本章节,我们以一个简单的计数器实例,介绍FPGA开发软件Vivado的基础操作流程,计数器实例程序的设计,在本课程中先不同掌握,后续课程将针对verilog编程进行讲解。 打开Vivado软件,会看到如下的界面: 点击如下图所示的: 点击Next> 将这个地方,设置路径,注意路径必须是英文路径。且文件夹名称第一个字符不能为数字。所有路径的名称的字符数量不能超过144个符号。 点击Next> 点击Next> 点...

FPGA/MATLAB学习教程/源码/项目合作开发 4478

基于Simulink的单级式光伏并网系统MPPT控制仿真

本文基于Simulink平台,详细介绍了单级式光伏并网系统的MPPT控制仿真实现。主要内容包括:1)阐述MPPT作为光伏系统"智能大脑"的核心作用;2)解析单级式系统架构及扰动观察法(P&O)算法原理;3)以10kW户用系统为例,分步骤演示建模过程,包括光伏阵列搭建、P&O控制器实现(含MATLAB代码)、并网控制设计等;4)通过仿真验证系统在光照突变下的动态响应性能,MPPT效率达98.3%。文章还提出了变步长P&O等优化方向,为光伏系统仿真与控制提供实用指导。

xiaoheshang_123的博客 111

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

上一篇: 【方法数超过64k限制时-如何配置多Dex打包?】
下一篇: 大Bitmap导致Android-java.lang.OutOfMemoryError
大雪冬至
博客等级 码龄7年 299粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值