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

news2024/9/29 1:18:01

经过上一篇《安卓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 开发中,你可以根据需求选择使用动态库或静态库。动态库适用于多个应用程序共享库文件、便于更新和维护的场景,而静态库适用于需要独立的可执行文件、性能优化和不需要依赖外部库的场景。

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

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

相关文章

机器学习概括

文章目录 一、机器学习是什么?二、模型训练YouTube流量预测1. 先写一个具有未知参数的函数(Function)2. 定义损失(从训练数据进行计算)3.最优化4.结果分析 Back to framework1.带有未知数的函数:2.定义损失…

20.自定义tabBar(使用vant中的tabbar)

自定义tabBar就是不用官方提供的tabBar了,而是自己搞 目录 1 配置 2 使用vant组件库做TabBar 2.1 引入组件 2.2 使用组件 2.3 自定义图标 2.4 把info与store进行绑定 2.5 tab页面的切换 2.6 改变激活状态下的文本颜色 1 配置 首先你需要在项目根…

4.2 “ P4G42_V1

P4G42_V1 产品参数 产品型号 P4G42_V1 尺寸(mm) 124*90*12mm(不含安装支架) 屏幕尺寸 4.2 inch 显示技术 电子墨水屏显示 显示区域(mm) 84.6(H) * 63.4(V) 分辨率(像素) 400*300 像素密度 120dpi 显示颜色 黑/白 外观颜色 白色&…

springMVC(二)—— 进阶

一、解决中文乱码问题 解决中文乱码问题的关键在于判断字符是什么时候乱码的 先在java程序里刚生成这个值的地方打印一下,如果在控制台输出就乱码了,那就排除浏览器和jsp页面的编码出问题。否则 看浏览器的编码 看这个jsp页面的编码是否设置好了 一般不用…

Python启动TCP服务并监听连接,从客户端发送消息

下面是一个简单的例子,演示如何在Python中启动TCP服务并监听连接,以及如何从客户端发送消息: TCP服务端代码: import socketHOST 192.168.6.211 PORT 8888server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) …

electron的(maximize)最大化方法会导致头部白色横条以及四周大黑边

参考文章:https://www.w3cways.com/2505.html 感谢这位博主 我在使用win.maximize()后发现应用程序虽然最大化了,但是四周和顶部有一定宽度的黑边白边,在百度一段时间后没有发现其他人有这个问题 😔唉 必现步骤: 只要…

禁止 input 输入框显示历史记录

问题概述 在 HTML 开发中会 input 标签是非常常用的, 当在 input 标签 输入数据后,再点击输入框会显示原来输入过的内容,还会出现下拉的历史记录。让使用人员感觉比较恶心。 解决方案(隐藏记录框) 隐藏记录框 aut…

环二肽试剂:7451-73-2,CYCLO(-GLY-TRP),环(甘氨酰-L-色氨酰)

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ CYCLO(-GLY-TRP)环二肽,含有(甘氨酰和色氨酰基团),色氨酸又名α-氨基吲哚基丙酸,分子式:C11H12N2O2,白色至黄白色晶体或结晶性粉末。无臭或微臭,长时间…

Apifox详细使用教程 -- 有这一篇就够了

文章目录 Apifox简介 Apifox的安装与新建项目 Apifox编写接口文档 总结 四、Apifox的使用快捷键 一、Apifox简介 是 API 文档、API 调试、API Mock、API 自动化测试一体化协作平台,更先进的 API 设计/开发/测试工具,定位 Postman Swagger Mock J…

SpringBoot + Kotlin 中使用 GRPC 进行服务通信

示例项目见:kotlin-grpc 一、导入依赖: import com.google.protobuf.gradle.* plugins { id("org.springframework.boot") version "2.3.1.RELEASE" id("io.spring.dependency-management") version "1.0.9.REL…

面试官常问的一些初中级前端知识点

CSS 1. 介绍下 CSS 盒子模型 回答到以下两点就可以: 一个盒子由四个部分组成:content、padding、border、margin。两种盒子模型:W3C 标准盒子模型、IE 怪异盒子模型标准盒子模型 width / height 只是内容高度,不包含 padding 和…

平板ipad触控笔是哪几款?苹果笔性价比高的推荐

实际上,现在市场上许多产品都有各自的特色,一些注重产品外观,一些注重产品功能。就相当于ipad上所搭配的电容笔。所以,在购买电容笔的时候,必须对电容笔有一些认识。在选购前,必须先了解各种类型的电容笔&a…

7-8 程序员买包子

7-8 程序员买包子 分数 10 全屏浏览题目 切换布局 作者 陈越 单位 浙江大学 这是一条检测真正程序员的段子:假如你被家人要求下班顺路买十只包子,如果看到卖西瓜的,买一只。那么你会在什么情况下只买一只包子回家? 本题要求你考…

线程 线程池

线程和进程的区别? 进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务 不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低 创…

博士后申请哪些老板不要选?

博士后申请是许多研究人员迈向学术界的重要阶段。在选择导师时,需要慎重考虑各方面因素。虽然我不能提及具体老板的名字,但我可以给你一些在博士后申请过程中需要谨慎选择的类型。以下是知识人网小编整理的一些可能不适合选择的老板类型: 1. …

Spring-AutowireFactoryBean

autowire autowire 即自动注入的意思,通过使用 autowire 特性,我们就不用再显示的配置 bean 之间的依赖了。把依赖的发现和注入都交给 Spring 去处理,省时又省力。autowire 几个可选项,比如 byName、byType 和 constructor 等。a…

刘铁猛C#教程学习笔记--初识C#程序

构成C#程序的基本元素 关键字:C#保留的用作特殊用途的标识符标识符:名字,如变量名字,类名字,命名空间名字等等; 标识符命名规则:只能以数字字母或者下划线开头,不能用关键字作为标识…

喜讯!云联壹云入选2023年北京市“专精特新”中小企业

6月25日,北京市经济和信息化局发布《关于对2023年第一季度北京市专精特新中小企业名单进行公告的通知》,北京云联壹云技术有限公司入选2023年北京市“专精特新”中小企业。 “专精特新”是国家为增强企业自主创新能力和核心竞争力,不断提高中…

HHDESK资源管理功能

为使客户体验更上一层楼,我们在最新的HHDESK版本中,新增了一个功能“资源管理”,其中不仅整合了原有的连接管理、网络代理功能,还增加了不少实用的新功能。 本篇便为您简单介绍一下。 1 本地服务 可以方便的调用本地资源。 比如…

基于STM32设计的酒精检测仪

一、需求分析 随着社会的发展和生活水平的提高,人们对于行车安全、家庭安全的要求越来越高,而酒驾等问题也日渐突出,为此,开发一款基于STM32的酒精检测仪,通过检测酒精浓度,实时显示结果并进行报警&#x…