现代化 Android 开发:基础架构

简介: Android 开发经过 10 多年的发展,技术在不断更迭,软件复杂度也在不断提升。到目前为止,虽然核心需求越来越少,但是对开发速度的要求越来越高。高可用、流畅的 UI、完善的监控体系等都是现在的必备要求了。国内卷的方向又还包括了跨平台、动态化、模块化。

Android 开发经过 10 多年的发展,技术在不断更迭,软件复杂度也在不断提升。到目前为止,虽然核心需求越来越少,但是对开发速度的要求越来越高。高可用、流畅的 UI、完善的监控体系等都是现在的必备要求了。国内卷的方向又还包括了跨平台、动态化、模块化。


目前的整体感觉就是,移动开发基本是奄奄一息了。不过也不用过于悲观:一是依旧有很多存量的 App 堪称屎山,是需要有维护人员的,就跟现在很多人去卷 framework 层一样,千万行代码中找 bug。 二是 AI 日益成熟,那么应用层的创新也会出现,在没有更简洁的设备出现前,手机还是主要载体,总归是需要移动开发去接入的,如果硬件层越来越好,模型直接跑在手机上也不是不可能,所以对跨平台技术也会是新一层的考验,有可能直接去跨平台化了。毕竟去中台化也成了历史的选择。


因而,在这个存量市场,虽然竞争压力很大,但是如果技术过硬,还是能寻求一席之地的。因而我决定用几篇文章来介绍下,当前我认为的现代化 Android 开发是怎样的。其目录为:

  • 现代化 Android 开发:基础架构(本文)
  • 现代化 Android 开发:数据类
  • 现代化 Android 开发:逻辑层
  • 现代化 Android 开发:组件化与模块化的抉择
  • 现代化 Android 开发:多 Activity 多 Page 的 UI 架构
  • 现代化 Android 开发:Jetpack Compose 最佳实践
  • 现代化 Android 开发:性能监控

Scope


提到 Android 基础架构,大家可能首先想到的是 MVCMVPMVVMMVI 等分层架构。但针对现代化的 Android 开发,我们首要有的是 scope 的概念。其可以分两个方面:


  • 结构化并发之 CoroutineScope:目前协程基本已经是最推荐的并发工具了,CoroutineScope 的就是对并发任务的管理,例如 viewModelScope 启动的任务的生命周期就小于 viewModel 的存活周期。
  • 依赖注入之 KoinScope:虽然官方推荐的是 hilt,但其实它并没有 koin 好用与简洁,所以我还是推荐 koinKoinScope 是对实例对象的管理,如果 scope 结束, 那
  • scope 管理的所有实例都被销毁。

一般应用总会有登录,所以大体的 scope 管理流程图是这样的:

6897b0855ca9090f8d7efe125da0a6d.png

  • 1.我们启动 app, 创建 AppScope,对于 koin 而言就是用于存放单例,对于协程来说就是全局任务
  • 2.当我们登录后,创建 AuthSessionScope, 对于 koin 而言,就是存放用户相关的单例,对于协程而言就是用户执行相关的任务。当退出登录时,销毁当前的 AuthSessionScope,那么其对应的对象实例、任务全部都会被销毁。用户再次登录,就再次重新创建 AuthSessionScope。目前很多 App 对于用户域内的实例,基本上还是用单例来实现,退出登录时,没得办法,就只能杀死整个进程再重启, 所以会有黑屏现象,实现不算优雅。而用 scope 管理后,就是一件很自然而实现的事情了。所以尽量用依赖注入,而不要用单例模式
  • 3.当我们进入界面后,一般都是从逻辑层获取数据进行渲染,所以依赖注入没多大用了。而协程的 lifecycleScopeviewModelScope 就比较有用,管理界面相关的异步任务。


所以我们在做架构、做某些业务时,首要考虑 scope 的问题。我们可以把 CoroutineScope 也作为实例存放到 KoinScope 里,也可以把 KoinScope 作为 Context 存放到 CorutineScope 里。


岐黄小筑是将 CoroutineScope 放到 koin 里去以便依赖查找

val sessionCoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob() + coroutineLogExceptionHandler(TAG))
val sessionKoinScope = GlobalContext.get().createScope(...)
sessionKoinScope.declare(sessionCoroutineScope)

其实我们也完全可以用 CoroutineScopeContext 来做实例管理,而移除 koin 的使用。但是 Context 的使用并没有那么便捷,或许以后它可以进化为完全取代 koin

架构分层


随着软件复杂度的提升,MVCMVPMVVMMVI 等先后被提出,但我觉得目前所有的开发,都大体遵循某一模式而又不完全遵循,很容易因为业务的节奏,很容易打破,变成怎么方便怎么来。所以使用简单的分层 + 足够优秀的组件化,才是保证开发模式不被打破的最佳实践。下图是岐黄小筑的整体架构图:

f71feeeb8f56af1ff62bad084bb0ed0.png

整体架构不算复杂,其实重点是在于组件库,emo 已经有 20 个子库了,然后岐黄小筑有一些对于通用逻辑的抽象与封装,使得逻辑层虽然都集中在 logic 层,但整体都是写模板式的代码,可以面向 copy-paste 编程。


BookLogic 为例:

// 通过依赖注入传参, 拿到 db 层、网络层、以及用户态信息的应用
class BookLogic(
    val authSession: AuthSession,
    val kv: EmoKV,
    val db: AccountDataBase,
    private val bookApi: BookApi
) {
    // 并发请求复用管理
    private val concurrencyShare = ConcurrencyShare(successResultKeepTime = 10 * 1000L)
    // 加载书籍信息,使用封装好的通用请求组件
    fun logicBookInfo(bookId: Int, mode: Int = 0) = logic(
        scope = authSession.coroutineScope, // 使用用户 session 协程 scope,因为有请求复用,所以退出界面,再进入,会复用之前的网络请求
        mode = mode,
        dbAction = { // 从 db 读取本地数据
            db.bookDao().bookInfo(bookId)
        },
        syncAction = { // 从网络同步数据
            concurrencyShare.joinPreviousOrRun("syncBookInfo-$bookId") {
                bookApi.bookInfo(bookId).syncThen { _, data ->
                    db.runInTransaction {
                        db.userDao().insert(data.author)
                        db.bookDao().insert(data.info)
                    }
                    SyncRet.Full
                }
            }
        }
    )
    // 类似的模板代码
    suspend fun logicBookClassicContent(bookId: Int, mode: Int = 0) = logic(...)
    suspend fun logicBookExpoundContent(bookId: Int, mode: Int = 0) = logic(...)
    ...
}
//将其注册到 `module` 中去,目前好像也可以通过注解的方式来做,不过我还没采用那种方式:
scopedOf(::BookLogic)

ViewModel 层浮层从 Logic 层读取数据,并可以进行特殊化处理:

class BookInfoViewModel(navBackStackEntry: NavBackStackEntry) : ViewModel() {
    val bookId = navBackStackEntry.arguments?.getInt(SchemeConst.ARG_BOOK_ID) ?: throw RuntimeException("book_id is required!.")
    val bookInfoFlow = MutableStateFlow(logicResultLoading<BookInfoPojo>())
    init {
        viewModelScope.launch {
            runInBookLogic {
                logicBookInfo(bookId, mode).collectLatest {
                    bookInfoFlow.emit(it)
                }
            }
        }
    }
}

Compose 界面再使用 ViewModel

@ComposeScheme(
    action = SchemeConst.ACTION_BOOK_INFO,
    alternativeHosts = [BookActivity::class]
)
@SchemeIntArg(name = SchemeConst.ARG_BOOK_ID)
@Composable
fun BookInfoPage(navBackStackEntry: NavBackStackEntry) {
    LogicPage(navBackStackEntry = navBackStackEntry) {
        val infoVm = schemeActivityViewModel<BookInfoViewModel>(navBackStackEntry)
        val detailVm = schemeViewModel<BookDetailViewModel>(navBackStackEntry)
        val bookInfo by infoVm.bookInfoFlow.collectAsStateWithLifecycle()
        //...
    }
}

这样整个数据流从网络加载、到存储到数据库、到传递给 UI 进行渲染的整个流程就结束了。


对于其中更多的细节,例如逻辑层具体是怎么封装的?UI 层具体是怎么使用多 ActivityPage?可以期待下之后的文章。

目录
相关文章
|
3天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
8天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
8天前
|
Android开发 Swift iOS开发
深入探索iOS与Android操作系统的架构差异及其对应用开发的影响
在当今数字化时代,移动设备已经成为我们日常生活和工作不可或缺的一部分。其中,iOS和Android作为全球最流行的两大移动操作系统,各自拥有独特的系统架构和设计理念。本文将深入探讨iOS与Android的系统架构差异,并分析这些差异如何影响应用开发者的开发策略和用户体验设计。通过对两者的比较,我们可以更好地理解它们各自的优势和局限性,从而为开发者提供有价值的见解,帮助他们在这两个平台上开发出更高效、更符合用户需求的应用。
|
18天前
|
API 持续交付 开发者
后端开发中的微服务架构实践与挑战
在数字化时代,后端服务的构建和管理变得日益复杂。本文将深入探讨微服务架构在后端开发中的应用,分析其在提高系统可扩展性、灵活性和可维护性方面的优势,同时讨论实施微服务时面临的挑战,如服务拆分、数据一致性和部署复杂性等。通过实际案例分析,本文旨在为开发者提供微服务架构的实用见解和解决策略。
|
2天前
|
消息中间件 监控 持续交付
后端开发中的微服务架构设计与实践####
在当今快速发展的软件开发领域,微服务架构已成为构建高效、可扩展和易于维护应用的关键策略。本文将深入探讨微服务架构的核心概念、设计原则与实战技巧,通过实例解析如何在后端开发中有效实施微服务,以应对复杂业务需求和技术挑战。我们将从微服务的拆分策略、通信机制、数据管理到持续集成/持续部署(CI/CD)流程,全面剖析其背后的技术细节与最佳实践,为读者提供一份详尽的微服务架构设计与实践指南。 ####
|
10天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
10天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
9天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS的系统架构差异
本文旨在通过对比分析Android和iOS两大移动操作系统的系统架构,揭示它们在设计理念、安全性、应用生态及开发环境等方面的显著差异。我们将从底层架构出发,逐步剖析至用户界面层面,为开发者和科技爱好者提供一份详尽的技术参考。
20 1
|
12天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
10天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
下一篇
无影云桌面