2023年Android现代开发

2023年现代Android开发

下面与大家分享如何构建具有2023年最新趋势的Android应用程序。
Android modern dev

Android是什么?

Android 是一种基于 Linux 内核并由 Google 开发的开源操作系统。它用于各种设备,包括智能手机、平板电脑、电视和智能手表。

目前,Android 是世界上移动设备使用最多的操作系统;根据statcounter的一份最近 12 个月的样本报告,Android 的市场份额为71.96%。

接下来,我将提及我认为对于在 Android 上构建现代应用程序很重要的工具、库、体系结构、指南和其他实用程序的列表。

Kotlin

Kotlin 是由JetBrains开发的一种编程语言。由谷歌推荐,谷歌于 2017 年 5 月正式宣布Kotlin作为Android开发语言。它是一种与 Java 兼容并可以运行在 JVM 上的现代编程语言,这使得它在 Android 应用程序开发中的采用速度非常快。
kotlin

不管你是不是Android新手,你都应该把Kotlin作为你的首选,不要逆水行舟🏊🏻😎,Google在Google I/O 2019上宣布了这种做法。有了Kotlin,你将可以使用所有现代语言的特性,包括协程的强大功能和使用为 Android 生态系统开发的现代库。

Kotlin官网
https://kotlinlang.org/docs/home.html

Jetpack Compose

Jetpack Compose 是 Android 推荐的用于构建原生 UI 的现代工具包。它简化并加速了 Android 上的 UI 开发。
jetpack compose

Jetpack Compose 文档
https://developer.android.com/jetpack/compose

Jetpack Compose 是 Android Jetpack 库的一部分,使用 Kotlin 编程语言轻松创建原生用户界面。此外,它还与其他 Android Jetpack 库(例如 LiveData 和 ViewModel)集成,以便更轻松地构建响应式和可维护的 Android 应用程序。

Jetpack Compose 的一些主要功能包括:

  1. 声明式用户界面。
  2. 可定制的小部件。
  3. 易于与现有代码集成。
  4. 实时预览。
  5. 改进的性能。

资源

  • 官方文档
    https://developer.android.com/jetpack/compose

  • Jetpack Compose 路线图
    https://developer.android.com/jetpack/androidx/compose-roadmap

  • 课程
    https://developer.android.com/courses/jetpack-compose/course

  • Compose 到 Kotlin 兼容性映射
    https://developer.android.com/jetpack/androidx/releases/compose-kotlin

Android Jetpack

Jetpack 是一套库,可帮助开发人员遵循最佳实践、减少样板代码并编写可跨 Android 版本和设备一致工作的代码,以便开发人员可以专注于他们关心的代码。
Jetpack

Android Jetpack 文档
https://developer.android.com/jetpack

它的一些最常用的工具是:

  • ViewModel
    https://developer.android.com/topic/libraries/architecture/viewmodel

  • Room
    https://developer.android.com/training/data-storage/room

  • DataStore
    https://developer.android.com/topic/libraries/architecture/datastore

  • WorkManager
    https://developer.android.com/topic/libraries/architecture/workmanager

  • Navigation
    https://developer.android.com/guide/navigation

  • CameraX
    https://developer.android.com/training/camerax

  • Compose
    https://devjorgecastro.medium.com/modern-android-app-development-in-2023-ff445d3652b4#af4e

Material Design

MD
Material Design 是一个适应性强的指南、组件和工具系统,支持用户界面设计的最佳实践。在开源代码的支持下,Material Design 简化了设计人员和开发人员之间的协作,并帮助团队快速构建精美的产品。

官网
https://m3.material.io/

Material Design 得到了 Google 的设计师和开发人员的支持,这将使我们有一个指南来处理 Android、Flutter 和 Web 的 UI/UX。

目前,Material Design 的最新版本是 3。

Clean Arch

Clean arch

它基于通过将软件划分为层来实现职责分离。

特征

  1. 独立于框架。
  2. 可测试。
  3. 独立于用户界面。
  4. 独立于数据库。
  5. 独立于任何外部机构。
  6. 依赖规则

作者在他的文章

The Clean Code Blog中很好地描述了依赖规则
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

使该体系结构起作用的最重要规则是依赖规则。这条规则说源代码依赖只能指向内部。内圈中的任何人都无法对外圈中的事物一无所知。特别是,在外圈中声明的名称不得在内圈中的代码中提及。这包括函数、类。变量,或任何其他命名的软件实体。

Clean Arch Blog
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

Android 中的Clean Arch

  • 表示:活动、片段、视图模型、其他视图组件。
  • 域:用例、实体、存储库和其他域组件。
  • 数据:存储库实现、映射器、DTO 等。

Android Clean Arch

Presentation Layer的架构模式

架构模式是一种更高级别的策略,旨在帮助设计软件架构,其特点是在可重用框架内为常见架构问题提供解决方案。架构模式类似于设计模式,但它们的规模更大,并且解决更多的全局问题,例如系统的整体结构、组件之间的关系以及数据的管理方式。

在表示层中,我们有一些架构模式,其中我想强调以下几点:

  • MVVM
  • MVI

Android 架构指南
https://developer.android.com/topic/architecture
MVI arch

依赖注入

依赖注入是一种软件设计模式,它允许客户端从外部源获取其依赖项,而不是自己创建它们。它是一种用于在对象及其依赖项之间实现控制反转 (IoC) 的技术。

  • Hilt ❤️
    https://developer.android.com/training/dependency-injection/hilt-android
  • Dagger
    https://dagger.dev/
  • Koin
    https://insert-koin.io/

模块化

模块化是一种软件设计技术,它允许您将应用程序划分为独立的模块,每个模块都有自己的功能和责任。
Modularization

模块化的好处

可重用性:通过拥有独立的模块,它们可以在应用程序的不同部分甚至其他应用程序中重用。

严格的可见性控制:模块使您能够轻松控制向代码库的其他部分公开的内容。

可定制的交付:Play Feature Delivery使用应用程序包的高级功能,允许您有条件地或按需交付应用程序的某些功能。

可扩展性:通过拥有独立的模块,可以在不影响应用程序其他部分的情况下添加或删除功能。

易于维护:通过将应用程序划分为独立的模块,每个模块都有自己的功能和职责,代码更容易理解和维护。

易于测试:通过拥有独立的模块,它们可以被隔离测试,这使得检测和修复错误变得容易。

架构改进:模块化有助于改进应用程序的架构,从而更好地组织和构建代码。

改善协作:通过拥有独立的模块,开发人员可以同时处理应用程序的不同部分,而不会受到干扰。

构建时间:一些 Gradle 功能,例如增量构建、构建缓存或并行构建,可以利用模块化来提高构建性能。

更多信息参考官方文档
https://developer.android.com/topic/modularization

网络

  • OkHttp
    https://square.github.io/okhttp/
  • Retrofit
    https://square.github.io/retrofit/

序列化

在本节中,我想提一下我认为的两个重要工具:Moshi与 Retrofit 和Kotlin Serialization一起广泛使用,这是 Jetbrain 的 Kotlin 团队的赌注。

  • Moshi
    https://github.com/square/moshi

  • Kotlin Serialization
    https://github.com/Kotlin/kotlinx.serialization

Moshi和Kotlin Serialization是 Kotlin 和 Java 的两个序列化/反序列化库,允许您将对象转换为 JSON 或其他序列化格式,反之亦然。两者都提供了针对移动和桌面应用程序优化的用户友好界面。Moshi 主要关注 JSON 序列化,而 Kotlin 序列化支持各种序列化格式,包括 JSON。

图片加载

要从 Internet 加载图像,可以使用多个第三方库来帮助您处理该过程。图像加载库为您做了很多繁重的工作;它们处理缓存(因此您不会多次下载图像)和网络逻辑以下载图像并将其显示在屏幕上。

  • Android官方文档
    https://developer.android.com/jetpack/compose/graphics/images/loading

  • Coil
    https://github.com/coil-kt/coil#jetpack-compose

  • Glide
    https://bumptech.github.io/glide/int/compose.html

react及线程管理

Flow
当我们谈论反应式编程和异步过程时,我们的第一个选择是Kotlin Coroutines;多亏了Suspension Functions,Flow我们可以满足所有这些需求。但是,我相信在本节中值得强调RxJava甚至在 Android 应用程序开发中的重要性。对于我们这些已经使用 Android 多年的人来说,我们知道 RxJava 是一个非常强大的工具,具有大量用于处理数据流的功能。我仍然认为 RxJava 是今天值得考虑的一个有趣的替代方案。

  • Kotlin Coroutines(suspend 及Flow API)
    https://github.com/Kotlin/kotlinx.coroutines/blob/master/README.md#using-in-your-projects
  • RxJava
    https://github.com/ReactiveX/RxJava

本地存储

LocalStore
构建移动应用程序的一个重点是能够在本地持久保存数据,例如一些会话数据或缓存数据等。根据您的应用需求选择正确的存储选项非常重要。我们可以存储键值对等非结构化数据或数据库等结构化数据。请记住,这一点并未提及我们可用的所有类型的本地存储(例如文件存储),仅提及允许我们保存数据的工具。
LocalStore

  • DataStore
    https://developer.android.com/topic/libraries/architecture/datastore
  • EncryptedSharedPreferences
    https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

测试

Testing

  • JUnit 5
    https://junit.org/junit5/
  • Mockk
    https://mockk.io/ANDROID.html
  • Espresso
    https://developer.android.com/training/testing/espresso
  • Robolectric
    https://robolectric.org/

R8 optimizations

R8 是默认编译器,可将项目的 Java 字节码转换为可在 Android 平台上运行的 DEX 格式。它是一种工具,通过缩短类及其属性的名称,消除项目中未使用的代码和资源,帮助我们混淆和减少应用程序的代码。要了解更多信息,请查看有关收缩、混淆和优化您的应用程序的Android 文档。

https://developer.android.com/studio/build/shrink-code

R8

  • Code shrinking
  • Resource shrinking
  • Obfuscation
  • Optimization

Play Feature Delivery

Google Play 的应用服务模型称为 Dynamic Delivery,它使用 Android App Bundle 为每个用户的设备配置生成和提供优化的 APK,因此用户仅下载运行您的应用所需的代码和资源。

Android文档
https://developer.android.com/guide/playcore/feature-delivery

Play Feature Delivery

自适应布局

Adaptive layouts
Adaptive layouts
随着具有不同形状因素的移动设备使用的增长,我们需要有工具使我们能够使用适应不同类型屏幕的 Android 应用程序。这就是 Android 为我们提供Window Size Classes的原因,简单来说,它是三大屏幕格式组,它们标记了我们开发设计的关键点。有了这个,我们避免了考虑许多屏幕设计的复杂性,将我们的可能性减少到 3 组,即:Compat、Medium和Expanded 。
Windows 尺寸等级
Window witdh
Window height
支持不同的屏幕尺寸
https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes

我们拥有的另一个重要资源是Canonical Layouts,它们是预定义的屏幕设计,可用于我们 Android 应用程序中的大多数场景,并向我们展示了如何使其适应大屏幕的指南。

Canonical Layouts

https://m3.material.io/foundations/adaptive-design/canonical-layouts

Canonical Layouts
其他一些资源
3 things to know about Form Factors at Google I/O 2022
https://android-developers.googleblog.com/2022/05/form-factors-google-io-22.html

Playlist: Form Factors at Google I/O
https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_seN7mrwgU7mDYKA0hq_ib

Form-Factor Training
https://developer.android.com/courses/pathways/jetpack-compose-for-android-developers-5

Form Factors at Google I/O 2022 (2)
https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_seN7mrwgU7mDYKA0hq_ib

性能表现

Performance
当我们为 Android 开发应用程序时,我们必须确保更好的用户体验,不仅在应用程序开始时,而且在整个执行过程中。出于这个原因,拥有使我们能够对可能影响应用程序性能的情况进行预防性分析和持续监控的工具非常重要,因此这里列出了可以帮助您实现此目的的工具:

  • Benchmark
    https://developer.android.com/topic/performance/benchmarking/benchmarking-overview
  • Baseline Profiles
    https://developer.android.com/topic/performance/baselineprofiles/overview
  • App Startup
    https://developer.android.com/topic/libraries/app-startup
  • Firebase Performance Monitoring
    https://firebase.google.com/docs/perf-mon
  • JankStats library
    https://developer.android.com/topic/performance/jankstats

应用内更新

当您的用户让您的应用程序在他们的设备上保持最新时,他们可以尝试新功能,并从性能改进和错误修复中受益。尽管某些用户在其设备连接到不按流量计费的连接时启用后台更新,但可能需要提醒其他用户安装更新。应用内更新是一项 Google Play 核心库功能,可提示活跃用户更新您的应用。

运行 Android 5.0(API 级别 21)或更高版本的设备支持应用内更新功能。此外,仅 Android 移动设备、Android 平板电脑和 Chrome 操作系统设备支持应用内更新。

_应用内更新文档
https://developer.android.com/guide/playcore/in-app-updates
app update

应用内评论

Google Play 应用内评论 API 可让您提示用户提交 Play 商店评分和评论,而无需离开您的应用或游戏。

通常,应用内评论流程可以在应用的整个用户旅程中随时触发。在此流程中,用户可以使用 1 到 5 星系统对您的应用进行评分并添加可选评论。提交后,评论将发送到 Play 商店并最终显示。

为保护用户隐私并避免 API 滥用,您的应用应遵循有关何时请求应用内评论和评论提示设计的严格指南。

应用内评论文档

https://developer.android.com/guide/playcore/in-app-review

In-App Reviews

辅助功能

Accessibility
辅助功能是软件设计和构造中的一个重要功能,除了改善用户体验外,它还为需要辅助功能的人提供使用应用程序的能力。该概念旨在改善的一些残障人士包括:有视力问题、色盲、听力问题、灵活性问题和认知障碍等的人。

注意事项:

  • 增加文本可见性(颜色对比度、可调整大小的文本)
  • 使用大而简单的控件
  • 描述每个 UI 元素

辅助功能官方文档
https://developer.android.com/guide/topics/ui/accessibility

安全

Security
安全性即使不是最重要的方面,也是我们在开发保护设备完整性、数据安全性和用户信任的应用程序时必须考虑的因素,这就是为什么我在下面列出一系列帮助您实现此目的的提示。

  • 加密敏感数据和文件:使用EncryptedSharedPreferencesEncryptedFile
  • 应用基于签名的权限:
  • 在您可以控制的应用程序之间共享数据时,使用基于签名的权限。
< manifest  xmlns:android = "http://schemas.android.com/apk/res/android" 
    package = "com.example.myapp" > 
    < permission  android:name = "my_custom_permission_name" 
                android:protectionLevel = "signature" / >
  • 不要将应用程序配置所需的密钥、令牌或敏感数据直接放在项目存储库中的文件或类中。请改用local.properties

Version Catalogs

Gradle 提供了一种集中管理项目依赖项的标准方式,称为版本目录;7.0版本试验引入,7.4版本正式发布。

优势:

  • 对于每个目录,Gradle 都会生成类型安全的访问器,以便您可以在 IDE 中轻松添加具有自动完成功能的依赖项。
  • 每个目录对构建的所有项目都是可见的。它是声明依赖项版本并确保对该版本的更改适用于每个子项目的中心位置。
  • 目录可以声明依赖包,它们是通常一起使用的“依赖组”。
  • 目录可以将依赖项的组和名称与其实际版本分开,并使用版本引用代替,从而可以在多个依赖项之间共享版本声明。

Catalogs更多信息
https://docs.gradle.org/current/userguide/platforms.html

Logger

logger是一种软件工具,用于记录有关程序执行的信息;重要事件、错误、调试消息和其他可能有助于诊断问题或了解程序工作方式的信息。记录器可以配置为将消息写入不同的位置,例如日志文件、控制台、数据库,或者通过将消息发送到日志服务器。

Timber
https://github.com/JakeWharton/timber

Linter

Linter
Linter 是一种编程工具,用于分析程序源代码以发现代码中潜在的问题或错误。这些问题可能是句法、不合适的代码风格、缺乏文档、安全问题等等,它们会对代码的质量和可维护性产生影响。

  • Android Lint
    https://medium.com/swlh/what-is-android-lint-17fa0d87abb2
  • Detekt
    https://detekt.dev/
  • Ktlint
    https://pinterest.github.io/ktlint/

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

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

相关文章

JVM-栈详解二

前言 虚拟机栈概述 虚拟机栈出现的背景 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实…

【Java项目】完善基于Java+MySQL+Tomcat+maven+Servlet的博客系统

目录一、准备工作二、引入依赖三、创建必要的目录四、编写代码五/六、打包部署(直接基于 smart tomcat)七、验证代码正式编写服务器代码编写数据库相关的操作代码创建数据库/表结构(数据库设计)数据库代码封装数据库操作封装针对数据的增删改查&#xff01;博客列表页约定前后端…

【论文阅读总结】用于目标检测的特征金字塔网络(FPN)

Feature Pyramid Networks for Object Detection1.摘要2.引言2.1 低级特征对于检测小物体很重要2.2 算法目标3. 文献综述3.1 Hand-engineered features and early neural networks3.2 Deep ConvNet object detectors3.3 Methods using multiple layers4.Feature Pyramid Networ…

嵌入式:BSP的理解

BSP概念总结BSP定义BSP的特点BSP的主要工作BSP在嵌入式系统和Windowsx系统中的不同BSP和PC机主板上的BIOS区别BSP与 HAL关系嵌入式计算机系统主要由 硬件层&#xff0c;中间层&#xff0c;系统软件层和应用软件层四层组成。硬件层&#xff1a;包含CPU&#xff0c;存储器(SDRAM&…

Mybatis(一):环境搭建

Mybatis&#xff08;一&#xff09;&#xff1a;环境搭建前言一、MyBatis简介1、MyBatis历史2、MyBatis特性3、MyBatis下载4、和其它持久化层技术对比二、搭建MyBatis1、开发环境2、创建maven工程2.1 打包方式&#xff1a;jar2.2 引入依赖3、创建MyBatis的核心配置文件4、创建m…

通俗简介:操作系统之进程的管理与调度

操作系统是一个复杂的软件&#xff0c;具备许多功能。其中&#xff0c;进程的管理与调度是与我们密切相关的。本文将对操作系统功能中进程管理与调度作出介绍。 目录 一、进程 二、 进程管理 1、进程管理的概念 2、进程结构体的核心属性 3、进程调度 &#xff08;1&#…

如何将pdf文件压缩?pdf压缩软件哪个好

PDF是一种常见的文档格式&#xff0c;因为包括文本格式和图像&#xff0c;我们往往采用这种格式进行文件传输和分享&#xff0c;但是也常常会因为pdf文件过大导致使用起来非常不方便&#xff0c;那么如何如何将pdf文件压缩&#xff08;https://www.yasuotu.com/pdfyasuo&#x…

禁用非必需插件,让 IDEA 飞起

文章首发于个人博客&#xff0c;欢迎访问关注&#xff1a;https://www.lin2j.tech IDEA 为我们提供了众多的插件&#xff0c;但是这些插件并不都是必须的。如果电脑的性能不够强&#xff0c;反而会带来一些不必要的资源消耗。 因此这里整理了一些不常用的插件&#xff0c;可以…

如何让AI帮你干活-娱乐(3)

背景今天的话题会偏代码技巧一些&#xff0c;对于以前没有接触过代码的朋友或者接触代码开发经验较少的朋友会有些吃力。上篇文章介绍了如何广视角的生成相对稳定的视频。昨天的实现相对简单&#xff0c;主要用的是UI界面来做生成。但是生成的效果其实也显而易见&#xff0c;不…

【个人首测】百度文心一言 VS ChatGPT GPT-4

昨天我写了一篇文章GPT-4牛是牛&#xff0c;但这几天先别急,文中我测试了用GPT-4回答ChatGPT 3.5 和 Notion AI的问题&#xff0c;大家期待的图片输入也没有出现。 昨天下午百度发布了文心一言&#xff0c;对标ChatGPT&#xff0c;录屏无实机演示让百度股价暴跌。但是晚上百度就…

不要迷信 QUIC

很多人都在强调 QUIC 能解决 HoL blocking 问题&#xff0c;不好意思&#xff0c;我又要泼冷水了。假设大家都懂 QUIC&#xff0c;不再介绍 QUIC 的细节&#xff0c;直接说问题。 和 TCP 一样&#xff0c;QUIC 也是一个基于连接的&#xff0c;保序的可靠传输协议&#xff0c;T…

【测试开发篇4】测试模型

目录 一、软件测试V模型 编码前 概要设计&#xff1a; 详细设计&#xff1a; 编码后&#xff1a; 单元测试&集成测试 系统测试 验收测试 V模型的特点 优点&#xff1a; 缺点&#xff1a; 二、软件测试W模型 编码之前&#xff1a; 编码的时候&#xff1a; 编…

全网最详细,Jmeter性能测试数据写入文件(总结)看这篇就够了......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 jmeter 性能测试数据…

RK3568平台开发系列讲解(Linux系统篇)消息队列

🚀返回专栏总目录 文章目录 一、创建消息队列二、发送和接收消息三、内核结构沉淀、分享、成长,让自己和他人都能有所收获!😄 📢消息队列在如下两个方面上比管道有所增强: 消息队列中的数据是有边界的,发送端和接收端能以消息为单位进行交流,而不再是无分隔的字节流…

Android---动态权限申请

目录 权限分类 动态权限核心函数 简易实现案例 完整代码 Google 在 Android 6.0 开始引入了权限申请机制&#xff0c;将所有权限分成了正常权限和危险权限。App 每次在使用危险权限时需要动态的申请并得到用户的授权才能使用。 权限分类 系统权限分为两类&#xff1a;正常…

队列实现及leetcode相关OJ题

上一篇写的是栈这一篇分享队列实现及其与队列相关OJ题 文章目录一、队列概念及实现二、队列源码三、leetcode相关OJ一、队列概念及实现 1、队列概念 队列同栈一样也是一种特殊的数据结构&#xff0c;遵循先进先出的原则&#xff0c;例如&#xff1a;想象在独木桥上走着的人&am…

计算机网络管理 TCP三次握手的建立过程,Wireshark抓包分析并验证TCP三次握手建立连接的报文

⬜⬜⬜ ---&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; (*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;---⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &#x1f381;欢迎各位→…

【Linux入门篇】操作系统安装、网络配置

目录 &#x1f341;Linux详解 &#x1f342;1.操作系统 &#x1f342;2.操作系统组成 &#x1f342;3.操作系统历史 &#x1f342;4.常见的Linux系统 &#x1f342;5.centos7下载 &#x1f342;6.安装centos7 &#x1f341;linux初始化配置 &#x1f343;1.虚拟机系统安装后操作…

从零实现深度学习框架——学习率调整策略介绍

引言 本着“凡我不能创造的,我就不能理解”的思想,本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架,该框架类似PyTorch能实现自动求导。 要深入理解深度学习,从零开始创建的经验非常重要,从自己可以理解的角度出发,尽量不使用外部完备的框架前提下,实现我…

【微信小程序】-- 案例 - 自定义 tabBar(四十六)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…