【29】Android之学习native开发(一)

news2024/9/23 17:17:59

一、概述

没什么好讲的了,Android学习成长过程必经之路就是了解Framework层的源码及原理,在跟踪流程过程中,难免遇到很多natvie函数,这个时候学习native能帮助我们更轻松的读懂这方便的代码。

这篇文章也会从最基础的东西开始讲起,Android的native开发基础是什么,那就是JNI和NDK的概念。当然还会涉及到C++语言的一些基础知识。

二、JNI & NDK

JNI(java native Interface)顾名思义,就是java本地接口。Java虚拟机提供的一种能力,能够提供接口帮助开发者调用到本地代码库(native lib)。
如下图:Java虚拟机运行时数据区
在这里插入图片描述
构建一个JNI项目
构建一个JNI项目差点让这篇文章未半而中道崩殂,建议没有过经验的同学第一次直接创建一个C++项目,和原本项目比较差异,再在已有项目上添加JNI功能。需要的环境搭建这里就不多讲了。
在这里插入图片描述
这里就直接讲如何在已有的项目上添加JNI,首先是模块的build.gradle文件,再android节点中添加cmake的文件索引和依赖版本,我目前使用的AS是| 2024.1.2 Beta 1,如果你也是使用这个版本,那么添加如下,仅此而已。

	android {
		...
		externalNativeBuild {
        	cmake {
            	path file('src/main/cpp/CMakeLists.txt')
            	version '3.22.1'
        	}
    	}
	}

接着在我们的项目中写一个JNI的调用,这里以官方例子为例。Kotlin和Java有所不同的是,Kotlin当中修饰native方法的关键字是external,而静态块的库加载方式则是在companion object的init中完成的。

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("nativelib");
    }

    private Button button;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.tv_button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                button.setText(stringFromJNI());
            }
        });
    }

    public native String stringFromJNI();
}

在build.gradle中我们声明的CMakeLists.txt的目录是在模块的*src/main/cpp/*下,所以创建这样一个cpp文件夹,并创建CMakeLists.txt文件。关于定义不同库的作用,之后再讲,这里使用的是动态库。

cmake_minimum_required(VERSION 3.22.1)
project("nativelib")
# SHARED:共享库(动态库) 共享库在运行时被动态加载。
# STATIC:静态库 静态库在链接时会被复制到目标可执行文件中。
# MODULE:模块库 模块库在运行时被动态加载,但不用于链接其他目标。
add_library(${CMAKE_PROJECT_NAME} SHARED native-lib.cpp)
target_link_libraries(${CMAKE_PROJECT_NAME} android log)
  • 第一个是cmake的最小要求版本
  • 第二个是项目名称,可以理解为生成的so文件名,也就是给到system.loadlibrary的文件名
  • 第三个是添加cpp文件,${CMAKE_PROJECT_NAME}就是指代上面的nativelib
  • 第四个是目标链接库,这里链接了android 、log两个库,都是由NDK提供的

Tip:cmake文件是txt文件,所以在编写的过程不会有错误提示,刚开始在已有项目构建的过程就是这个文件编写出错,导致一直提示错误,而且不容易被发现。

最后创建需要的native-lib.cpp文件,并实现JNI的接口方法,也就是我们定义的native方法。

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_nativelib_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

这段代码就算没有学过native开发应该也能猜出,把定义的hello字符串返回。这样我们Clean Project一下项目,再重新Rebuild Project一下就构建完成了。切换项目目录结构到Android结构下,看到有cpp文件目录并且生成了includes的文件夹,就能说明构建成功。
在这里插入图片描述
run一下这个项目,会出来一个带有按钮的界面,点击按钮,按钮的文字就会更新成Hello from C++。
在这里插入图片描述
刚刚讲了一下Java最基础的JNI方式来调用本地方法,接着说一下NDK(Native Development Kit)本地开发工具,是Android提供的一套工具和库,用于在Android应用中使用C和C++代码。事实上natvie开发范围很大,而NDK可以理解为native的一个子集。

在上面CMake文件中有提到过链接NDK的库,那如何知道NDK有提供哪些库呢,可以在本地NDK安装目录中查看包含的库。

$ANDROID_SDK/ndk/<ndk-version>/platforms/android-<api-level>/arch-<arch>/usr/lib

在CMake或ndk-build脚本中,你可以使用find_library命令查找NDK库。例如:
find_library(log-lib log)
find_library(android-lib android)
这些命令会查找并链接NDK中的log和android库。

这是一个完整的例子

cmake_minimum_required(VERSION 3.4.1)

# 添加本地库
add_library(native-lib SHARED native-lib.cpp)

# 查找NDK库
find_library(log-lib log)
find_library(android-lib android)

# 链接NDK库
target_link_libraries(native-lib ${log-lib} ${android-lib})

最后做个总结,JNI是Java提供的一种接口来调用本地方法的一种方式,而NDK则是帮助我们在Android中使用C/C++代码开发,目的是提高应用的安全性、性能和效率。

三、指针

在学习C++语言中,指针这个概念特别难懂,还好Java没有指针,不然刚开始学习的成本就太高了。我会从我是一个新手的角度来讲,在了解指针过程中,我面临的问题以及需要搞懂的问题。

在C语言中,指针是需要分配内存空间的,由于c代码不会自动释放内存,所以在使用指针之后,不再需要使用都需要手动释放内存空间。了解指针基础,应该都知道指针指的是对象的内存地址。

int var = 10;
int *ptr = &var;

printf("Value of var: %d\n", var);      // 输出:10
printf("Address of var: %p\n", &var);   // 输出:var的地址
printf("Value of ptr: %p\n", ptr);      // 输出:ptr存储的地址(即var的地址)
printf("Value pointed by ptr: %d\n", *ptr);  // 输出:10

第一行是声明了一个变量var,并且给它赋值10。第二行,左边是声明了一个指向int类型的指针,右边是将var变量的地址赋值给指针。&符号就是用来获取对象地址的符号。倘若写成 int *ptr = var,那么它的意思就是讲var的值作为地址赋值给指针,但是由于10不是一个标准的地址格式,因此会报错。

可以看到只有使用*ptr,表示内存地址保存的数据,而直接使用ptr打印的就是地址信息。这里还有一点很重要,int *ptr = &var分配了两块内存空间,一块用来存储数据10,另一块内存空间来存储var的地址。

这里再介绍一下指针类型,指向不同类型的数据,包括基本数据类型、数组、结构体、函数等。

char *charPtr;
float *floatPtr;
double *doublePtr;

int arr[5] = {1, 2, 3, 4, 5};
int *arrPtr = arr;  // 等价于 int *arrPtr = &arr[0];

因为数组名本身就是一个指向数组第一个元素的指针,所以不需要使用&符号来索引数组的地址。

struct Person {
    char name[50];
    int age;
};

struct Person person;
struct Person *personPtr = &person;

指向结构体,当需要使用age的时候可以通过 *(personPtr).age方式获取age,简化写法personPtr->age。当然也可以直接操作person结构体变量来获取age,即person.age。请记住,这里是创建了一个person结构体变量,然后指针指向这个结构体变量的地址,所以操作这个结构体变量和通过操作指针指向的地址的内容,都是指的同一个东西。

由于person变量没有初始化值,这个时候打印结构体,string会默认为空字符串,基本数据类型,没有显式初始化可能会输出一个随机值。

int var = 10;
int* ptr = &var;
int** ptr2 = &ptr; // 指向指针的指针

void func() {
    std::cout << "Hello, World!" << std::endl;
}
void (*funcPtr)() = &func; // 定义一个指向函数的指针
funcPtr(); // 调用函数

int* ptr = new int; // 动态分配一个 int 类型的内存
*ptr = 10;
delete ptr; // 释放内存

最后指针的三个高级用法,指向指针的指针,通过*ptr2获取到的是ptr指针的地址,因此再通过/*号就可以获取到var内存地址的数据。

函数指针这里了解一下使用即可,同样需要定义函数的指针类型,这个类型和函数的返回值有关,而指针后面的括号,则是函数的参数。

动态分配,通过new的方式自动分配无数据内容的一块内存,并将内存地址赋值给指针。

总结一下,指针在定义的时候*表示接收的内存地址,使用指针的时候*表示地址的内容,使用&对象的时候,表示对象的地址。

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

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

相关文章

Towards Self-supervised Learning on Graphs with Heterophily

推荐指数: #paper/⭐ 发表于: CIKM22 一句话总结:本文通过重构相似性矩阵来生成两种特征增强方法.通过对比学习来进行训练.值得一题的一点是他在对比学习中,用了X得到的特征和AX的对比.还是有一丢丢创新性的 主要工作: 图增强 特征相似矩阵 S ~ i j F cos ⁡ ( x i , x j )…

数据结构与算法--队列

文章目录 提要队列的定义队列的认识队列的应用队列的抽象数据类型队列的存储结构队列的链式存储结构与实现链队的进队和出队操作链队的数据类型初始化链队列入队操作出队操作队列的顺序存储结构与实现顺序队列的假溢出问题队列上溢循环队列循环队列取下一相邻单元下标运算队满与…

U盘数据丢失?一招教你如何使用四种技巧轻松找回!

每一个打工人可能都是被各种文件所困扰的&#xff0c;而且现在不仅仅是工作上&#xff0c;还有学习以及日常的生活记录也需要接触到各类的数据&#xff0c;拿我们平时用软件时产生的文件、图片、视频等等来说&#xff0c;就占据了磁盘的大部分空间&#xff0c;当然有时候也会选…

让玄学可靠:构建复杂 LLM 应用

ChatGPT 从 2023 年一月份爆火&#xff0c;到了六月份热度下降&#xff0c;大量的 Chat 应用昙花一现&#xff0c;很多人又开始讨论——大模型到底能解决什么问题&#xff1f;过去太多的焦点给到了 ChatGPT&#xff0c;让大家以为 AI ChatGPT&#xff0c;而忽略了背后的 LLM。…

作业练习1

要求&#xff1a;R1-R2-R3-R4-R5 RIP 100 运行版本2 R6-R7 RIP 200 运行版本1 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1-R2之间增加路由传递…

vLLM初识(一)

vLLM初识&#xff08;一&#xff09; 前言 在LLM推理优化——KV Cache篇&#xff08;百倍提速&#xff09;中&#xff0c;我们已经介绍了KV Cache技术的原理&#xff0c;从中我们可以知道&#xff0c;KV Cache本质是空间换时间的技术&#xff0c;对于大型模型和长序列&#xf…

MyBatis动态代理和映射器

目录 1、映射器简介 &#xff08;1&#xff09;什么是mapper动态代理&#xff1f; &#xff08;2&#xff09;动态代理的规范 &#xff08;3&#xff09;如何使用动态代理 &#xff08;4&#xff09;为什么学映射器 &#xff08;5&#xff09;映射器与接口 &#xff08;…

动手学深度学习V2每日笔记(卷积层)

本文主要参考沐神的视频教程 https://www.bilibili.com/video/BV1L64y1m7Nh/p2&spm_id_from333.1007.top_right_bar_window_history.content.click&vd_sourcec7bfc6ce0ea0cbe43aa288ba2713e56d 文档教程 https://zh-v2.d2l.ai/ 本文的主要内容对沐神提供的代码中个人不…

3.OpenFeign与负载均衡

文章目录 什么是 OpenFegin0penFeign 与 Ribbon.对 consumer 的改造超时配置请求响应的压缩设置选择远程调用的底层实现技术OpenFegin 整合 LoadBalancer 负载均衡负载均衡策略的更换小结 前面消费者对于微服务的消费是通过 RestTemplate 完成的,这种方式的弊端是很明显的:消费…

清华计算几何-算法LowBound和ConvexHull(凸包)-GrahamScan

算法复杂度最低界限LowBound 算法求解复杂度是否存在一个最低界限&#xff0c;有时候想尽一切办法优化一个算法&#xff0c;去优化其复杂度&#xff0c;比如 清华计算几何-ConvexHull(凸包)-求极点InTriangle/ToLeft Test-CSDN博客 清华计算几何-ConvexHull(凸包)-求极边_计…

5.0-软件工程基础知识-考点分析

考试占比大概10分 软件工程概述软件过程模型 瀑布模型 瀑布模型变种-V模型 演化模型-原型模型 增量模型 喷泉模型 基于构件的开发模型 形式化方法模型需求分析系统设计系统测试运维软件质量和度量项目管理系统分析与设计概念结构化分析WebApp设计与用户界面设计

【小技巧】Keil5 和 NotePad++ 代码格式化 (Ctrl + Q) ( 插件安装astyle-3.5-x64 / NppAStyle)

Artistic Style 是适用于 C、C、C/CLI、 Objective-C 、C# 和 Java 编程语言的源代码缩进器、格式化器和美化器。它用 C 编写&#xff0c;可以从命令行使用&#xff0c;也可以作为库合并到另一个程序中。可以从命令行或选项文件输入选项。可以从用 C 以外的语言编写的程序中调用…

【qiankun微前端】基座主应用(vue2)+多个微应用(任意框架)

前言 前段时间对我们已有的工程进行了微前端改造,后来思考一下微前端的本质,查询了不少资料,从qiankun微前端示例中学到了不少。 微前端的核心,似乎应该是一个基座应用(含登录页,layout页,404和首页等),多个子应用(任意框架,提供内部页面内容),下面就对这个思路…

预警器件控制思考

预警器件控制思考 最小示例思想 当读取到环境信息与环境阈值的时候, 我们预警系统就要根据这些信息做出判断,是否要启动器件。 最简单的就是&#xff0c; 举温度temp的例子, temp(温度)与temp_th(阈值), 通过判断, 得出是否要启动器件. 如果在一段时间内, 一直是环境异常, 我…

私藏心术:低谷期翻身转运秘籍

私藏心术&#xff1a;低谷期翻身转运秘籍 在生活中&#xff0c;每个人都可能遇到低谷期&#xff0c;那些看似无法逾越的障碍和挫折。但记住&#xff0c;低谷期不是终点&#xff0c;而是重新站起来的起点。本文将分享一些实用的心术和策略&#xff0c;帮助你在低谷期实现翻身转…

第一个 Flask 项目

第一个 Flask 项目 安装环境创建项目启动程序访问项目参数说明Flask对象的初始化参数app.run()参数 应用程序配置参数 安装环境 mkvirtualenv flask_envpip install flask创建项目 启动程序 访问项目 http://127.0.0.1:5000/ 参数说明 Flask是一个用Python编写的轻量级Web应…

程序员学长 | 快速学习一个算法,CLIP

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;快速学习一个算法&#xff0c;CLIP 今天给大家介绍一个强大的算法模型&#xff0c;CLIP。 CLIP (Contrastive Language–Image Pre-training) 是一个…

机器学习流程图

第一部分&#xff1a;课程使用的技术栈 &#xff08;1&#xff09;Numpy 科学计算基础库&#xff0c;矩阵运算&#xff0c;线性代数 &#xff08;2&#xff09;matplotlib 绘图库&#xff0c;数据可视化 &#xff08;3&#xff09;Scikit 封装了各种分类&#xff0c;回归…

【课程总结】day19(下):Transformer源码深入理解

前言 在上一章【课程总结】day19(下):Transformer架构及注意力机制了解总结中,我们对Transformer架构以及注意力机制有了初步了解,本章将结合《The Annotated Transformer》中的源码,对Transformer的架构进行深入理解。 背景 《The Annotated Transformer》是由 Harva…

LaneATT推理详解及部署实现(上)

目录 前言1. 概述2. 环境配置3. Demo测试4. ONNX导出初探5. ONNX导出优化6. ONNX导出总结结语下载链接参考 前言 最近想关注下车道线检测任务&#xff0c;在 GitHub 上找了一个模型 LaneATT&#xff0c;想通过调试分析 LaneATT 代码把 LaneATT 模型导出来&#xff0c;并在 tens…