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

ops/2024/9/23 7:32:19/

一、概述

没什么好讲的了,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;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = findViewById(R.id.tv_button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic 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.ppmy.cn/ops/89207.html

相关文章

Vue中输入框仅支持数字输入

方法 1: 使用 input 事件和正则表达式 通过监听 input 事件并使用正则表达式来验证输入&#xff0c;只允许输入数字。 <template><div><input type"text" v-model"inputValue" input"validateInput" /></div> </te…

前端-防抖代码

//防抖debounce(fn, time 1000) {let timer null;return function (...args) {if (timer) clearTimeout(timer);timer setTimeout(() > {fn.apply(this, args);}, time);};},// 输入变化处理函数async inputChange(value) {if (!this.debouncedInputChange) {this.deboun…

卷积神经网络 - 池化(Pooling)篇

序言 在深度学习的广阔领域中&#xff0c;卷积神经网络&#xff08; CNN \text{CNN} CNN&#xff09;以其卓越的特征提取能力&#xff0c;在图像识别、视频处理及自然语言处理等多个领域展现出非凡的潜力。而池化&#xff08; Pooling \text{Pooling} Pooling&#xff09;作为…

视频教程 - 自研Vue3 Tree组件高级功能:虚拟滚动新增节点实现自动滚动

感谢小伙伴们对本套自研vue3 tree组件教程的关注&#xff0c;在前一篇媲美Element Plus JuanTree终极实战&#xff1a;虚拟滚动的功能演示中发现了小bug&#xff0c;特地整理了相关录屏来说明怎么一步步解决bug的&#xff0c;来回馈小伙伴们的支持。 Tree组件高级功能&#xff…

计算机网络--网络层串讲

笔记整理自学习开源文档&#xff1a;小林coding 为什么要做这个串讲&#xff1f;因为我在学习408网络层的时候&#xff0c;看完机构的视频课感觉没有很好地将知识串联起来&#xff0c;便找到《图解网络》作为补充&#xff0c;且小林的讲解是我认为开源中较为通俗易懂的&#x…

python dash框架

Dash 是一个用于创建数据分析型 web 应用的 Python 框架。它由 Plotly 团队开发&#xff0c;并且可以用来构建交互式的 web 应用程序&#xff0c;这些应用能够包含图表、表格、地图等多种数据可视化组件。 Dash 的特点&#xff1a; 易于使用&#xff1a;Dash 使用 Python 语法…

8.3 字符串中等 306 Additive Number 423 Reconstruct Original Digits from English

306 Additive Number //累加数&#xff1a;除了前两个数&#xff0c;其余数都等于前两个加起来&#xff0c;至少包括三个数 //难点找到前两个数 //条件1&#xff1a;至少包括三个数–>确定前两个数字的最大长度 len n/3 看下方注意1 //条件2&#xff1a;遇到0默认归属于他…

Langchain核心模块与实战[9]:RAG检索增强生成[文本向量化、实战ChatDoc智能文档助手]

Langchain核心模块与实战[9]:RAG检索增强生成[文本向量化、实战ChatDoc智能文档助手] 参考文章可以使用国产LLM进行下述项目复现: 初识langchain[1]:Langchain实战教学,利用qwen2.1与GLM-4大模型构建智能解决方案[含Agent、tavily面向AI搜索]langchain[2]:Langchain实战教…