2023年Android开发者路线-第1部分
2023年Android开发者路线-第2部分
2023年Android开发者路线-第3部分
2023年Android开发者路线-第4部分
2023年Android开发者路线-第4部分
在上一篇博文中,我们讨论了现代 Android 开发的基本要素,包括 Fragments、App Navigation、Architecture Components 和 Android Jetpack 库。
在第4部分,我们将学习Android以下几个部分内容:
- Design Patterns 设计模式
- Architecture 架构
- Asynchronous 异步
- Network 网络
- Image Loading 图片加载
- Local Storage 本地存储
Design Patterns
软件设计模式是一种可重用的解决方案,用于解决软件工程中重复出现的、常见的软件问题。设计模式可以根据它们解决的问题类型进行分类,例如创建模式、行为模式和并发模式。
在Android开发中,经常会用到一些设计模式来解决Android平台常见的问题,管理资源的生命周期,比如Dependency Injection和Observer模式。
您可能已经至少使用过一次创建模式,例如Builder 模式、Factory 方法模式和Singleton 模式在 Android 开发中创建实例。
在本节中,您将探索影响现代 Android 开发中整体架构设计的三种主要模式。
依赖注入
依赖注入是现代 Android 开发中最流行的模式之一,它将创建对象的义务转移到类之外。
通过转移创建对象的义务,类之间不需要相互依赖。因此,您可以设计类之间松散耦合的依赖关系。
如果您正确使用依赖注入,您可以受益于以下优势:
- 减少样板代码。
- 类之间的松散耦合,因此您可以轻松编写单元测试。
- 利用类的可重用性。
- 提高代码的可维护性。
您可以按照手动依赖注入指南手动实施依赖注入,但强烈建议使用以下高效解决方案:Hilt、Dagger和Koin(严格的服务定位器模式)。
Hilt
:Hilt 是 Android 的编译时依赖注入工具,它在Dagger之上运行。与Dagger-Android库相比,Hilt 生成标准的 Android Dagger 组件并减少了所需的样板代码量。此外,Google 提供与ViewModel、 Jetpack Compose、 Navigation和WorkManager的兼容性,在撰写本文时,它是现代 Android 开发强烈推荐的依赖注入工具。有关详细信息,请查看Hilt文档中的依赖项注入。Dagger
:Dagger 也是一种编译时依赖注入工具,基于javax.inject注释和 ( JSR 330 )。你可以使用 Dagger 为你的 Android 项目配置依赖注入,但是这将需要大量额外的设置成本,因为 Dagger 不是一个 Android 依赖库。强烈建议使用Hilt或Dagger-Android来大幅降低设置成本。Koin
:Koin 也是Kotlin 项目中流行的依赖注入(严格来说是服务定位器模式)工具,它易于使用且设置成本低。有关更多信息,请查看insert-koin.io。
更多依赖注入的内容参考:https://developer.android.com/training/dependency-injection
观察者模式
观察者模式是一种行为设计模式,允许您构建订阅机制以自动通知观察者任何状态更改。
观察者模式也是 Android 开发中最常用的模式之一,用于在组件之间构建松散耦合的架构。它还可用于克服 Android 平台限制,例如独立 Android 组件之间的通信。
您可以实现自己的订阅功能,并且可以通过以下库轻松使用它们:RxKotlin、Kotlin Flows和LiveData。
-
LiveData:LiveData 是一种生命周期感知、线程安全和数据持有者观察者模式。LiveData 的观察者与Android Lifecycles绑定,所以你不需要手动取消订阅你的观察者,当生命周期未激活时,他们不会订阅数据发射。因此,它将防止无法预测和难以识别的内存泄漏。它还通过LiveData-ktx库提供有用的操作符,并支持数据绑定和Room兼容性。然而,现代 Android 开发更喜欢 Kotlin 的 Flows 而不是 LiveData,因为协程已被广泛采用。如果您有兴趣迁移到 Flow,请查看从 LiveData 迁移到 Kotlin 的 Flow.
-
Kotlin Flows:Flows 是一种异步解决方案,它是冷流,类似于使用Coroutines 的序列。它们是异步和非阻塞的解决方案,在 Kotlin 的语言级别得到支持。Flows 还提供了有用的运算符,例如Transform operator、 Flattening operators和flowOn operator。您可以利用Android 中的StateFlow和SharedFlow来实现状态持有者可观察流并将值发送给多个消费者。
-
RxKotlin (RxJava):RxKotlin 起源于ReactiveX,是观察者模式、迭代器模式和函数式编程的结合。RxKotlin 提供了许多有用的运算符,允许您使用可观察序列来编写异步和基于事件的程序。此外,您可以使用这些运算符轻松解决并发问题,例如低级线程、同步和线程安全,并且有许多适用于 Android 的有用解决方案,例如RxAndroid。然而,RxKotlin 包含许多运算符,对于初学者来说可能过于复杂。Kotlin Flows或LiveData更易于使用,尤其是当您不需要在项目中使用大量复杂操作时。
存储库模式
存储库模式起源于领域驱动设计,这是一种通过提供数据抽象来调解领域和数据的软件方法。
表示层使用具有近似业务逻辑的接口的简单抽象,例如从本地数据库查询数据和从网络获取远程数据。实际的实现类执行繁重的工作并执行与领域相关的工作。
存储库在概念上封装了与特定域相关的可执行域函数的集合,并为其他层提供更多面向对象的方面。
在现代 Android 开发中,数据层由存储库组成,这些存储库作为公共接口暴露给其他层,并遵循单一真实来源原则。
因此其他层可以将域数据作为流来观察,例如 Kotlin 的Flow或LiveData,并保证真实的来源。有关存储库模式和数据层的更多信息,请查看App Architecture。
现在让我们讨论 App架构。
Architecture
架构是现代Android开发中至关重要的一部分,它决定了项目管理的整体代码复杂度和成本。
随着项目功能数量的增加,代码行数和代码内聚性也相应增加。应用程序架构广泛影响着项目的复杂性、可扩展性和鲁棒性,并使测试变得更加容易。通过定义每个层之间的边界,您可以清楚地定义它们的职责,并通过将它们模块化为专门的角色来分离每个职责。
在历史上,Android的架构趋势在过去几年中发生了变化,这取决于可用的解决方案和最佳实践。
七八年前,Android项目使用MVC和MVP架构模式构建,但现在大多数项目使用MVVM和MVI架构模式,因为引入了有用的订阅解决方案,如RxKotlin、Kotlin Flows和LiveData。
在本节中,您将了解近年来最流行的架构模式:MVVM、MVI和Clean Architecture。
MVVM
自从谷歌正式公布Architecture Components以来,MVVM(Model-View-ViewModel)是现代 Android 开发中最流行的架构设计之一,例如ViewModel、LiveData和Data Binding。
从历史上看,自从Microsoft 引入Model-View-ViewModel 模式以来,它已经被 WPF 开发人员使用了十多年。
MVVM 模式由View、ViewModel和Model组成,如下图所示:
每个组件在Android开发中都有不同的职责,定义如下:
-
View:负责构建用户界面,显示在屏幕上供用户看到。View由包括TextView、Button或Jetpack Compose UI等UI元素的Android组件组成。UI元素触发用户事件传递给ViewModel,并通过观察来自ViewModel的数据或UI状态来配置UI屏幕。理想情况下,View只包含代表屏幕和用户交互的UI逻辑,例如监听器,不包含业务逻辑。
-
ViewModel:这是一个独立的组件,不依赖于View,它保存来自Model的业务数据或UI状态,并将其传播到UI元素中。通常情况下,ViewModel和Model之间存在多个(一对多)关系,ViewModel将数据更改通知给View作为领域数据或UI状态。在现代Android开发中,谷歌建议使用ViewModel库,该库有助于开发人员轻松保存业务数据并在配置更改时保留状态。但从技术上讲,您可以说它与Microsoft的ViewModel不同,因为它与Microsoft的设计原意不太相似。为使其接近Microsoft的版本,您应该利用其他解决方案,如数据绑定和订阅机制解决方案,例如RxKotlin、Kotlin Flows和LiveData。有关更多详细信息,请查看Microsoft的“模型-视图-视图模型模式”。
-
Model:封装应用程序的领域/数据模型,通常包括业务逻辑、复杂的计算工作和验证逻辑。Model类通常与远程服务和本地数据库一起使用,这些库在库中封装了数据访问,如可执行领域函数的集合。仓库确保多个数据源的单一真相和整体应用程序数据的不变性。
如果您想探索使用MVVM架构和上述设计模式构建的开源项目,请查看GitHub上的Pokedex。
[MVVM开源项目Pokedex] https://github.com/skydoves/pokedex
MVI
MVI(Model-View-Intent)也是现代 Android 开发中流行的架构,因为Jetpack Compose将声明式编程带入了我们的生活。
MVI 模式侧重于单一事实源原则,为其他层提供不可变状态,以及表示用户操作和配置 UI 屏幕结果的状态的单向和不变性。
MVI 架构是基于 MVP 或 MVVM 等其他架构设计的状态管理机制,这意味着 MVI 架构可以根据你的设计在 Presenter 或 ViewModel 上运行。
与 MVVM 和 MVP 不同,MVI 的每个组件的定义略有不同:
-
Intent:Intent 是处理用户操作(UI 事件,如按钮单击事件)的接口和函数的定义。这些函数将 UI 事件转换为 Model 的接口并将结果传递给 Model 进行操作。顾名思义,我们可以说我们有意执行 Model 函数。
-
Model:在 MVI 中,Model 的定义与 MVP 和 MVVM 完全不同。在 MVI 中,Model 是一个功能机制,它接受来自 Intent 的输出并将其操作为可在 View 中呈现的 UI 状态。UI 状态是不可变的,并来自业务逻辑,它遵循单一真理源和单向数据流。
-
View:MVI 中的 View 具有与 MVP 和 MVVM 相同的职责,表示屏幕和用户交互,例如侦听器,并且不包含业务逻辑。与其他模式的实现最大的不同之一在于,MVI 确保单向数据流,因此 View 根据来自 Model 的 UI 状态呈现 UI 元素。
如果你对学习更多关于 MVI 的内容感兴趣,Hannes Dorfmann 的《使用模型-视图-意图进行响应式应用程序开发》一书将帮助你更好地掌握 MVI 架构。
如果你想探索一个使用 MVI 架构和上述设计模式构建的开源项目,请查看 GitHub 上的 WhatsApp Clone Compose。
Clean Architecture
Clean Architecture 是由 Robert C. Martin (Uncle Bob) 在他的 Clean 书系之一 “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” 中引入的,他提出了一些面向对象编程 (OOP) 范式的设计方法来构建强大且清晰的应用程序结构。
自从依赖注入解决方案(如 Dagger)和多模块项目环境被引入以来,Clean Architecture 在现代 Android 开发中被广泛使用。此外,该理论可以与其他架构一起使用,例如 MVP、MVVM 和 MVI。
这种架构带来以下优点:隔离模块、增加可重用性、提高可扩展性以及易于编写单元测试用例。但是,如果你正在开发一个不需要复杂业务逻辑的小项目,这种架构可能过于复杂,因此你应该调查一下这种架构是否对你的项目有优势。
在深入了解清洁架构理论之前,我们将讨论SOLID设计原则,这些原则是Uncle Bob在他的一系列干净书中介绍的。Robert提出了以下五个软件设计原则,使您可以构建易于理解、灵活和可维护的项目:
-
单一职责:每个软件组件,如类或模块,应该只有一个更改的原因;这意味着在同一个组件、类或模块中设计不相关的功能会使代码的目的难以理解并且不清晰。
-
开闭原则:您应该能够扩展组件的功能(开放性)而不破坏其点并且不修改使用(封闭性)。
-
里氏替换:扩展类必须替换父类。这意味着父类必须具有提供简洁目的的最小接口,子类必须实现父类的每个抽象。
-
接口隔离:正如您可以通过名称估算的那样,它是一种面向原子的原则,可以与单一职责和里氏替换原则相关联。最好创建许多较小的接口而不是一个巨大的接口,以防止功能臃肿并违反里氏替换原则。
-
依赖反转:类和模块必须依赖于抽象,而不是具体实现,以实现单向和线性化的依赖性。此外,这确保了组件的纯净性,这意味着每个组件负责其专用角色。不要将其与依赖注入设计模式混淆。
清洁架构基本上遵循上述SOLID设计原则。Uncle Bob将清洁架构描述如下图所示:
圆心是最纯粹的范围,没有依赖于其他层。每个层必须暴露抽象以供外部层使用,这些外部层具有内部圆形依赖关系。正如您可能已经注意到的那样,这是 SOLID 设计原则的最大化组合。
每个层都有自己的单一职责,并遵循它们之间的依赖反转原则。现在让我们看看每个层的职责:
- 实体:封装应用程序的一组业务规则和对象。该层还遵循最高级别的规则,并公开抽象以供其他层轻松使用。在 Google 的官方架构指南中,您可以将实体层视为数据层。
- 用例:用例包含应用程序业务规则的定义,例如将在实体层中触发功能的用户操作。该层仅依赖于实体层,并向外部层公开抽象以执行特定于应用程序的业务逻辑。
- 展示者(接口适配器):该层执行所有暴露的接口,这些接口是来自用例层的应用程序业务规则的定义,并与 UI 层通信。在 MVVM 中,ViewModel 属于此处。
- UI(框架和驱动程序):UI 层表示 UI 如何呈现,包括 Activity、Fragment 和所有 UI 元素,例如用于 Android 屏幕上的 Android 部件。
如果您想了解更多关于这个概念并在您的Android项目中使用它,可以参考以下资料:
《The Clean Architecture》https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
《Architecting Android…The clean way?》https://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
《Clean Architecture Tutorial for Android: Getting Started》https://www.raywenderlich.com/3595916-clean-architecture-tutorial-for-android-getting-started
Asynchronous
Asynchronous and Concurrency
在Android中,系统会创建一个主线程(也称为UI线程),负责处理应用程序的所有与UI相关的工作。主线程负责渲染UI元素、将事件分派给适当的用户界面以及Android UI工具包组件之间的所有交互。
因此,如果你想执行I/O或耗费大量计算工作(如网络请求和查询数据库),你应该在另一个线程(所谓的工作线程)中处理它们。这确保了主线程专注于渲染屏幕和处理用户交互。
话虽如此,编写一个高度多线程的程序很难维护和调试,还有其他考虑因素,如避免竞态条件和管理资源。幸运的是,有解决方案可以执行计算密集型的业务工作而不会阻塞主线程,并且它们使得不需要手动逐个处理每个线程成为可能。
现在让我们看看如何使用以下解决方案执行业务逻辑。
RxJava/RxKotlin
使用RxJava的优点之一是,它可以轻松地控制多线程问题,比如在后台线程中执行业务逻辑并在UI线程中获取结果。
RxJava提供了一个线程池,称为Schedulers,并且该线程池包含以下不同类型的线程:io、computation、single或者你可以创建一个全新的线程。
你可以通过使用SubscribeOn和ObserveOn运算符来指定Schedulers来操纵线程行为,这些运算符定义了哪个线程应该工作,哪个线程应该消耗结果。
如果你想了解更多关于RxJava和多线程的知识,可以参考Aritra Roy的《Multi-Threading Like a Boss in Android With RxJava 2》。
https://github.com/ReactiveX/RxJava
Coroutines
Coroutines是一种在语言级别上异步执行代码的优秀并发解决方案。
https://kotlinlang.org/docs/coroutines-overview.html
与线程不同,协程是纯粹的用户级语言抽象,因此它不直接绑定于操作系统资源,每个协程对象都在JVM堆中分配。这意味着协程由用户控制,消耗的资源更少,上下文切换的成本比线程低。
根据Android文档,协程很轻量级,因此您可以在单个线程上运行多个协程,并且因为它们基于作用域工作,所以导致的内存泄漏较少。Google还支持许多Jetpack库的集成和兼容性,例如ViewModelScope和LifecycleScope,如果您选择协程作为并发解决方案,您将获得许多优势。
https://developer.android.com/kotlin/coroutines
如果您想了解有关Android的协程的更多信息,请查看《Kotlin协程在Android上》。
https://developer.android.com/kotlin/coroutines
Network
网络通信是现代应用的重要组成部分。然而,构建自己的网络解决方案需要大量资源,如连接池、响应缓存、HTTP(超文本传输协议)特定功能、拦截器和异步调用支持。
7到8年前,Android开发人员使用HttpURLConnection或Apache的HttpClient执行HTTP请求。然而,这些库需要大量样板代码,并且不支持Android平台的兼容性,如连接特性、安全支持和DNS(域名系统)解析。
在本节中,我们将探讨Android最受欢迎的HTTP库:OkHttp和Retrofit。
OkHttp
OkHttp是由Square开发的HTTP客户端,为JVM和Android构建,并使用内部的现代I/O库Okio。它默认情况下可以高效地工作,帮助你快速建立HTTP客户端。当网络有问题(如连接问题)时,它有自己的恢复系统,所以你不需要手动处理。该库提供了现代的TLS(传输层安全)功能、Android的安全支持、缓存和拦截器。
OkHttp中最有能力的功能之一是拦截器,它是一个强大的机制,允许你记录、监视、修改、重写和重试调用。
你可以根据需要轻松地转换所有网络请求,例如在头部添加访问令牌(例如 Bearer 认证)或将 Gzip 压缩添加到请求正文中。
https://github.com/square/okhttp
Retrofit
Retrofit 是一个类型安全的 HTTP 客户端,适用于 Android 和 JVM,也是由 Square 开发的。Retrofit 在 OkHttp 之上提供了抽象层,允许您轻松、简洁地定义请求规范,无需处理底层实现。
您还可以通过支持 Retrofit 注释构建所需的 HTTP 请求,包括 URL、标头操作、请求方法和正文。此外,通过附加可插拔的 Converter.Factory,您可以轻松地序列化所有 JSON 响应,而无需编写样板代码。
您还可以通过附加 CallAdapter 来操纵网络响应,这使您可以处理原始响应并将响应类型建模为所需的类型。有关更多信息,请查看 Modeling Retrofit Responses With Sealed Classes and Coroutines。
如果您想了解更多关于 Retrofit 的信息,请参阅 Retrofit 的官方页面。
https://square.github.io/retrofit/
Image Loading
图片加载也是现代应用程序开发的重要组成部分,例如当从网络加载用户资料或其他内容时。
您可以实现自己的图像加载系统,但它需要许多功能,在幕后下载图像,调整大小,缓存,渲染和内存管理等等。
在本节中,我们将探讨流行的 Android 图像库。
Glide
Glide 是由 bumptech 开发的最流行的图像库之一,并已经流行了很长一段时间。它已被许多全球产品和开源项目使用,包括 Google 的官方开源项目。
它提供了一些有用的功能,例如动画 GIF 支持、占位符、变换、缓存和资源重用。
有关更多信息,请查看 Glide 的官方文档。
https://bumptech.github.io/glide/
Coil
Coil 是由 Colin White 创建的,自 2019 年以来越来越受欢迎。
它完全是用 Kotlin 编写的,并且公开的 API 对 Kotlin 友好。一个值得注意的点是,Coil 比其他替代方案更轻,因为它使用了 Android 项目中已经广泛使用的其他库,例如 OkHttp 和 Coroutines。
Coil 还支持 Jetpack Compose,它提供了有用的功能,例如转换、动画 GIF 支持、SVG 支持和视频帧支持。
有关更多信息,请查看 Coil 的官方指南。
https://coil-kt.github.io/coil/
Fresco
Fresco 也是流行的图像加载库之一,与 Glide 一样长期以来一直如此。它是由 Meta 开发的。
与其他库不同,Fresco 专注于有效地使用内存,特别是针对 Android 版本 4.x 以下的设备。然而,最近的项目至少要求最低 SDK 版本为 21 到 23,API 非常复杂。除非您正在构建内存敏感的应用程序,否则请选择 Glide 或 Coil。
有关更多信息,请查看 Fresco 的官方指南。
https://frescolib.org/
Landscapist
Landscapist是由Jaewoong(skydoves)开发的Jetpack Compose图像加载库,使用Glide、Coil和Fresco获取和显示网络或本地图像。
由于Jetpack Compose将UI渲染机制完全从原始的基于XML的方式改变了,因此开发了Landscapist来以通用的方式支持使用流行的图像加载库进行图像加载。
Landscapist支持跟踪图像状态、组合自定义实现以及动画,如圆形揭示和淡入淡出。最近的一个版本还引入了一个新概念叫做ImagePlugin,可以更轻松、更快速地附加和实现图像加载行为。
欲了解更多信息,请访问Landscapist的GitHub页面。
https://github.com/skydoves/landscapist
Local Storage
本地存储是 Android 中另一个常用的解决方案。如果您需要将用户输入或远程资源持久保存在用户的本地设备中,则应将它们保存和还原在本地存储中。
在本节中,我们将探索 Jetpack 的主要本地存储解决方案:Room 和 DataStore。
Room
Room 是由 Google 提供的 Android Jetpack 库,它提供了一个在 SQLite 上的抽象层,简化了查询和访问数据库而无需编写复杂的 SQL 语句。
它基于注释处理器(也支持 Kotlin 符号处理),因此实现(如查询和插入列)将在编译时生成。
使用这个库的最大优势之一是开发人员不需要学习 SQL 查询,因为抽象层非常简洁易懂。它还提供了有用的功能,如协程、RxJava 兼容性、自动迁移策略和类型转换器。
要了解更多关于 Room 的信息,请查看使用 Room 在本地数据库中保存数据培训。
DataStore
DataStore是Google提供的另一个Android Jetpack库,它是一种数据存储解决方案,可以让你在本地存储中存储键值对。这个库是SharedPreferences的另一个解决方案。
DataStore还支持与其他库的高度兼容性,如Coroutines和Flow,以异步方式存储数据,并支持RxJava。它还支持使用协议缓冲区存储类型化对象。
要了解有关DataStore的更多信息,请查看Google的官方文档。
https://developer.android.com/topic/libraries/architecture/datastore
结论
这就是2022年Android开发路线图的第四部分。这一部分涵盖了设计模式、架构、异步、网络、图像加载和本地存储,这些都是现代Android开发的必要组成部分。
《 2022 Android Developer Roadmap》https://github.com/skydoves/android-developer-roadmap