安卓JNI从0到1入门教程(二)

经过上一篇《安卓JNI从0到1入门教程(一)》介绍,我们对JNI有了初步认识,接下来我会从ndk-build方式和cmake方式分别来介绍怎么构建native库:

一、ndk-build

ndk-build依赖配置文件Android.mk,存放代码的位置通常是jni目录

1.新建一个java测试类

package com.example.jni;

public class JNIDemo {

    static {
        //这个库名必须跟Android.mk的LOCAL_MODULE对应
        System.loadLibrary("JniDemo");
    }

    public static native String test();
}

2.创建jni目录

3.生成.h文件

打开命令行,切换到项目的java目录,然后使用以下命令生成.h头文件。(没有javah命令的请先配置jdk环境变量)

javah -d ../jni com.example.jni.JNIDemo

生成的文件会在jni目录下出现

 4.实现具体C++函数

在jni目录下新建一个.cpp文件,内容如下

#include "com_example_jni_JNIDemo.h" //引入刚刚生成的头文件

extern "C"
//实现test函数实际功能
JNIEXPORT jstring JNICALL Java_com_example_jni_JNIDemo_test(JNIEnv * env, jclass clz){
    return env->NewStringUTF("hello world");
}

 5.配置Android.mk和Application.mk

在jni目录下新建这两个文件,文件内容如下:

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#定义要构建生成的本地库的名称,以下示例会生成名为 libJniDemo.so 的库。
LOCAL_MODULE := JniDemo

#指定源代码文件
LOCAL_SRC_FILES := \
JNITest.cpp \

include $(BUILD_SHARED_LIBRARY)

 Application.mk

#指定用于此应用的 C++ 标准库,默认情况下使用 system STL。其他选项包括 c++_shared、c++_static 和 none
APP_STL := c++_shared
APP_CPPFLAGS := -frtti -fexceptions
# APP_ABI:指定生成哪些cpu类型的so, all代表全部 常用的有:armeabi-v7a,arm64-v8a,x86,x86_64
APP_ABI := armeabi-v7a arm64-v8a
#APP_PLATFORM 会声明构建此应用所面向的 Android API 级别,并对应于应用的 minSdkVersion
APP_PLATFORM := android-21

注意:Gradle 的 externalNativeBuild 会忽略 APP_ABI。请在 splits 块内部使用 abiFilters 块或abi 块。

6.生成.so文件

你可以直接使用ndk-build命令构建,也可以在AndroidStudio中配置快捷方式。(本示例使用ndk版本为21.4.7075529)

(1)用构建命令构建
控制台切换到jni目录下,也就是包含Android.mk和Application.mk的目录,执行ndk-build命令,成功后可以在libs文件夹下找到。(没有ndk-build命令的需要先配置ndk环境变量)

(2)配置快捷工具

打开File-Settings-Tools-External Tools,添加新的工具,命名为ndk-build(随意命名),Program配置选择你的ndk所在的目录下的ndk-build.cmd,这个通常在你的AndroidStudio的安装目录下的ndk目录,Working directory填写项目的jni目录

点击执行ndk-build即可生成.so文件

 7.在代码中使用native方法

public class MainActivity extends AppCompatActivity {

    private TextView tvMsg;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvMsg = findViewById(R.id.tv_msg);
        //调用native方法
        String msg = JNIDemo.test();
        tvMsg.setText(msg);
    }
}

二、CMake

上面讲述了ndk-build,现在讲讲CMake怎么配置

1.添加CMakeLists.txt文件

在项目的app模块根目录新建CMakeLists.txt文件,填写如下内容:

#指定CMake的最低版本要求
cmake_minimum_required(VERSION 3.18.1)

# 定义本地库的名称
set(my_lib_name JniDemo)

#添加库配置,如果有多个库,可以添加多个add_library方法
add_library( # 指定生成库的名称,使用上面定义的变量
        ${my_lib_name}
        # 标识是静态库还是动态库 STATIC:静态库 SHARED:动态库
        SHARED
        # C/C++源代码文件路径
        src/main/cpp/JNITest.cpp)

#指定.h头文件的目录
include_directories(src/main/cpp/)

# 指定构建输出路径
set_target_properties(${my_lib_name} PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")

# 链接外部静态库(如果你的静态库或动态库依赖于其他库或依赖项)
target_link_libraries(MyStaticLibrary PRIVATE MyExternalStaticLibrary)

示例中的add_library函数用于指定要构建的本地库以及其源代码文件。target_link_libraries函数用于指定要链接的系统库,例如log库,它用于在本地代码中使用Android的日志功能。

最后,通过set_target_properties函数,可以指定生成的共享库的输出路径,将其放置在libs/${ANDROID_ABI}目录下,其中${ANDROID_ABI}是一个CMake变量,表示当前目标平台的ABI(应用二进制接口)

更多CMakeLists.txt的配置项请参考配置Cmake

2.在app模块下的build.gradle中添加externalNativeBuild和ndk配置

android {
    compileSdk 33

    defaultConfig {
        applicationId "com.example.jni"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            //指定编译的ABI平台
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }

    externalNativeBuild {
        cmake {
            //指定CMakeLists文件所在位置
            path "CMakeLists.txt"
        }
    }
  //...省略
}

3.添加C++文件

CMake通常使用cpp目录而不是jni目录,所以在src/main的目录下新建一个cpp目录,然后把.h和.cpp文件放进去

 4.生成.so文件

使用AS的Rebuild Project构建项目,即可以在libs目录生成对应so文件。

 好了,至此两种方式介绍已经讲完。

更多关于其中的配置可参考谷歌官方的文档。

后记

上述过程中,我们涉及了2个概念:动态库静态库

在NDK编译过程中,这是两个不一样库文件生成方式。

1.动态库(Dynamic Library):

  • 动态库是一种在运行时被加载和链接到应用程序中的库文件。在 Android NDK 中,动态库的文件扩展名通常为 .so(Shared Object)。
  • 动态库可以被多个应用程序或模块共享,以减少磁盘空间和内存占用。多个应用程序可以同时加载和使用同一个动态库。
  • 动态库的加载和链接是在运行时进行的,这意味着库文件可以在应用程序运行期间动态加载和替换,从而实现更新和维护的方便性。
  • 在 Android NDK 中,动态库使用 C/C++ 编译器生成,并使用 ndk-build 或 CMake 等构建工具进行编译和构建。

2.静态库(Static Library):

  • 静态库是一种在编译时被链接到应用程序中的库文件。在 Android NDK 中,静态库的文件扩展名通常为 .a(Archive)。
  • 静态库会在应用程序编译时被复制到可执行文件中,每个使用了静态库的应用程序都包含了库的完整副本。
  • 静态库的优点是可以提供独立的可执行文件,不需要依赖外部的库文件。它在性能上可能略优于动态库,因为静态库的函数调用是直接的,无需动态链接过程。
  • 在 Android NDK 中,静态库也使用 C/C++ 编译器生成,并使用 ndk-build 或 CMake 等构建工具进行编译和构建。

在 Android NDK 开发中,你可以根据需求选择使用动态库或静态库。动态库适用于多个应用程序共享库文件、便于更新和维护的场景,而静态库适用于需要独立的可执行文件、性能优化和不需要依赖外部库的场景。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI PPT 一句话搞定PPT讲演搞

相信大家在职场中,一定会接触过写PPT,经常会把你搞得焦头烂额。在大部分的公司里,写PPT汇报又是不可能逃避的事情。但随着AI时代的到来,有很多AI帮你写PPT的工具也逐渐崭露头角,可以自动帮助你制作出华丽的PPT&#xf…

docker部署rabbitmq 后访问管理首页常见问题

1.项目启动后 管理首页无法访问 1)检查15672端口是否可以访问 2)docker exec -it your_container_name /bin/bash 进入docker容器执行如下命令: 3) rabbitmq-plugins enable rabbitmq_management 2.访问首页时提示不是私密连接:…

自动化测试 selenium 篇

✏️作者:银河罐头 📋系列专栏:JavaEE 🌲“种一棵树最好的时间是十年前,其次是现在” 目录 什么是自动化测试?Selenium 介绍Selenium 是什么Selenium 特点工作原理 seleniumJava环境搭建ChromeJava1.下载ch…

抖音seo源码--矩阵系统开发者日志

这是矩阵系统源码开发者的日志分享,我们致力于为开发者们提供优质的SEO源码。我们研究用户行为、数据分析和搜索引擎算法,以提高内容的搜索排名和曝光度。通过不断优化关键词、元数据和链接策略,我们帮助抖音用户更好地被发现和分享。这个日志…

flutter开发实战-指纹、面容ID验证插件实现

flutter开发实战-指纹、面容ID验证插件实现 在iOS开发中,经常出现需要指纹、面容ID验证的功能。 指纹、面容ID是一种基于用生物识别技术,通过扫描用户的面部特征来验证用户身份。 一、效果图 二、iOS指纹、面容ID验证 在iOS中实现指纹、面容ID验证功能…

如何用爬虫实现GPT功能

如何用爬虫实现GPT功能? GPT(Generative Pre-trained Transformer)和爬虫是两个完全不同的概念和技术。GPT是一种基于Transformer模型的自然语言处理模型,用于生成文本,而爬虫是一种用于从互联网上收集数据的技术。 …

【数据结构与算法】力扣:对称二叉树

对称二叉树 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true 示例 2: 输入:root [1,2,2,null,3,null,3] 输出:false 来源:…

GlusterFs 分布式复制卷(Distributed-Replicate)性能测试

目录 fio工具参数解释 Glusterfs 和NFS 性能测试 顺序写: 随机写: 顺序读: 随机读: 随机读写: 参数说明: 测试结论: 与NFS对比 压测对比结果 NFS和GlusterFs的优缺点 NFS的优点 NFS…

看完这篇异地多活的改造,我决定和架构师battle一下

1. 简述 异地多活的概念以及为什么要做异地多活这里就不进行概述了。概念性的很多,像什么同城双活、两地三中心、三地五中心等等概念。如果有对这些容灾架构模式感兴趣的可以阅读下这篇文章进行了解:《浅谈业务级灾备的架构模式》。 阅读本篇文章之前&…

脚踏Java知识点

对上节Java的基础语法续讲 三元运算符和if语句格式的区别 语法格式: 三元运算符的语法格式是:(condition) ? expression1 : expression2; if语句的语法格式是: if (condition) { // 执行 expression1 } else { // 执行 express…

API全场景零码测试机器人——ATGen带来“超自动化”测试模式

HDC期间可参与新手入驻华为云Testplan抽奖活动,活动链接在文末 众所周知,软件服务及组件之间的交互主要依赖大量的API接口。以华为云300多个商用云服务为例,平均每个服务含500接口,接口总数高达10万,接口调用上下文业务…

多元回归预测 | Matlab基于鹈鹕算法(POA)优化径向基神经网络(POA-RBF)的数据回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于鹈鹕算法(POA)优化径向基神经网络(POA-RBF)的数据回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %% 清…

IDE /skipping incompatible xxx_d.dll when searching for -lxxx_d

文章目录 概述场景复现用以测试的代码编译器位数不匹配导致?保持编译器类型一致再验证编译器位数的影响MingW下调用OS的库咋不告警?以mingW下使用winSocket为例MingW下网络编程的头文件分析该环境下链接的ws2_32库文件在哪里?mingW为啥可以兼容window下的动态库 概…

【Flutter】Audioplayers 4.1.0 简要使用说明

文章目录 一、前言二、安装和设置三、基本使用1.创建 AudioPlayer 实例2.设置音频源3.控制播放 四、示例代码五、总结 一、前言 Audioplayers 是一个非常实用的 Flutter 插件,它可以帮助我们在 Flutter 应用中播放音频。无论你是想在你的应用中添加背景音乐&#x…

Docker: 改变容器化世界的革命性技术

目录 1.1什么是虚拟化 1.2什么是Docker 1.3容器与虚拟机的比较 1.4Docker组建 2、Docker安装 2.2设置ustc的镜像 2.3Docker的启动与停止 3、docker常用命令 3.1镜像 3.2容器相关命令 1.1什么是虚拟化 在计算机中,虚拟化(Vitualization&#x…

如何从一个仪表盘管理多个WordPress网站?

您是否正在寻找一种管理多个WordPress网站的简单方法? 监控多个网站并使其保持更新可能非常耗时。 幸运的是,有几种 WordPress 管理工具可以让您从单个仪表板管理多个 WordPress 网站变得非常容易。这将帮助您节省大量时间,同时使所有 Word…

赋能智能智造-RK3568智能主板助力机器人产业高速发展

机器人作为现代制造业的重要一环,正在以惊人的速度推动着生产效率和智能化水平的提升,它们在生产线上的准确操作和高效工作,为企业带来了巨大的竞争优势。关于工业机器人的编程和控制技术,在过去几年中已经有了很多发展和新的应用…

Coggle 30 Days of ML(23年7月)任务九:学会Bert基础,transformer库基础使用

Coggle 30 Days of ML(23年7月)任务九:学会Bert基础,transformer库基础使用 任务九:学会Bert基础,transformer库基础使用 说明:在这个任务中,你将学习Bert模型的基础知识&#xff…

【安全】Xsslabs(1~13)基于白盒测试浅析

目录 环境 关卡 level 1 level 2 level 3 level 4 level 5 level 6 level 7 level 8 扩展 level 9 level 10 level 11 level 12 level 13 总结 环境 PHP:php7.3.4nts 中间件:Nginx1.15.11 工具:Hackbar 关卡 level …

计网简答题

答案不保证正确性,仅供参考。 1.有如图所示的以太网,每个交换机的名字及接口号、主机的名字及MAC地址都标明在图中。网络初启动时,两个交换机的转发表都为空,接着先后进行以下MAC帧传输:H1→H5,H3→H2&…