更多关于 Android 中的 Clean Architecture:包含 Cross-cutting 特性的示例

发布: (2025年12月4日 GMT+8 06:41)
3 min read
原文: Dev.to

Source: Dev.to

介绍

现在我们来讨论跨整个应用的特性——异常、对话框、全局事件等。这些特性相互依赖。
核心思想是为全局事件创建一个 单一真实来源 (SSOT),并在主模块中订阅它。Kotlin Flows 能帮助我们实现这一点,我们将使用为此场景设计的 SharedFlow

要求

  • 平台:仅 Android(不含 CMP/KMP)
  • 项目:多模块
  • 每个特性都有自己的页面
  • 任意特性都可以发送触发器
  • 各特性相互独立

结构

总体方案

Scheme of modules

Domain 模块

Domain 模块是独立的,包含核心契约:

interface GlobalRepository {
    val eventFlow: SharedFlow
    fun sendTrigger(trigger: Trigger)
}

enum class Trigger {
    SOME_TRIGGER_1,
    SOME_TRIGGER_2,
    // …
}

enum class GlobalEvent {
    SOME_EVENT_1,
    SOME_EVENT_2,
    // …
}

Domain‑shared 模块

该模块依赖 :domain:core,并提供发送触发器的实现和 DI:

interface SendTrigger {
    operator fun invoke(trigger: Trigger) // or suspend fun if needed
}

class SendGlobalEventImpl @Inject constructor(
    private val repository: GlobalRepository
) : SendTrigger {
    override fun invoke(trigger: Trigger) = repository.sendTrigger(trigger)
}

Data 模块

使用 MutableSharedFlow 实现 GlobalRepository

class GlobalRepositoryImpl @Inject constructor(
    // injected entities such as dispatcher or scope if needed
) : GlobalRepository {
    private val _eventFlow = MutableSharedFlow()
    override val eventFlow = _eventFlow.asSharedFlow()

    override fun sendTrigger(trigger: Trigger) {
        when (trigger) {
            // some logic
            // example:
            // someScope.launch { _eventFlow.emit(event) }
            // other logic
        }
    }
    // …
}

Flow 参数(replay、buffer 等)可根据具体情况进行调优。

App 与特性模块

  • App 模块:直接从 :domain:core 注入 GlobalRepository,或暴露获取事件流的接口。
  • 特性模块:从 :domain:shared 注入 SendTrigger

特性模块的工作方式请参见上一篇文章

小结

该方案遵循了之前文章的相同原则:

  • 单一独立的 :domain:core,包含共享接口。
  • Data 模块充当全局事件的 SSOT

此模式与文章 Sharing data between ViewModels 中描述的类似,只是应用在更大范围。

警告: 示例仅适用于上述特定需求。它可能不适用于其他项目类型。请勿盲目复制。

感谢阅读!

Back to Blog

相关文章

阅读更多 »