You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

MVI架构下不同Intent处理器间共享通用状态逻辑的方案咨询

MVI架构下不同Intent处理器间共享通用状态逻辑的方案咨询

我之前在做Android MVI项目的时候,也碰到过完全一样的问题——每个页面单独定义的Sealed Intent里,导航、返回这类通用逻辑重复得满天飞,改起来要改N个地方,特别麻烦。下面是我实践下来觉得好用的几个方案,你可以根据自己项目的规模和复杂度来选:

方案一:通过继承复用通用Intent定义

最直接的方式是把所有通用的Intent抽成一个基类密封类,然后各个页面的Intent密封类去继承它。这样每个页面的Intent天然就包含了通用逻辑,不用重复定义。

代码示例:

首先定义通用的基类Intent:

sealed class CommonIntent {
    object NavigateToBack : CommonIntent()
    data class NavigateToUserInfo(val id: Int) : CommonIntent()
    // 其他通用的Intent,比如跳转到设置页、打开通知页都可以放这
}

然后让页面专属的Intent继承这个基类:

sealed class SettingIntent : CommonIntent() {
    data class BecomeAMaster(val status: StatusOfUser) : SettingIntent()
    // 这里不用再定义NavigateToUserInfo和NavigateToBack了,直接继承自CommonIntent
}

sealed class SearchIntents : CommonIntent() {
    data class ChangeText(val text: String) : SearchIntents()
    object GetData : SearchIntents()
    object OpenFilters : SearchIntents()
    object CloseFilters : SearchIntents()
    object ApplyFilters : SearchIntents()
    object ResetFilters : SearchIntents()
    data class FilterAction(val action: FilterIntent) : SearchIntents()
}

在ViewModel里处理的时候,只需要在when表达式里先处理通用Intent的逻辑,再处理页面专属的:

fun handleIntent(intent: CommonIntent) {
    when (intent) {
        is CommonIntent.NavigateToBack -> navController.popBackStack()
        is CommonIntent.NavigateToUserInfo -> navController.navigate("user_info/${intent.id}")
        // 处理页面专属Intent
        is SettingIntent.BecomeAMaster -> { /* 处理成为大师的逻辑 */ }
        is SearchIntents.ChangeText -> { /* 处理搜索文本变化 */ }
        // ...其他页面专属逻辑
    }
}

这种方式的好处是实现简单,几乎不用改原有代码结构,适合中小型项目。

方案二:通过组合方式嵌入通用Intent

如果不想让页面Intent和通用Intent有继承关系(比如担心基类太庞大,或者页面只需要部分通用Intent),可以用组合的方式——在页面Intent里定义一个专门的类型,用来包裹通用Intent。

代码示例:

先定义独立的通用导航Intent:

sealed class CommonNavigationIntent {
    object NavigateToBack : CommonNavigationIntent()
    data class NavigateToUserInfo(val id: Int) : CommonNavigationIntent()
}

然后在页面Intent里加入组合类型:

sealed class SettingIntent {
    data class toUserScreen(val id: Int) : SettingIntent()
    data class BecomeAMaster(val status: StatusOfUser) : SettingIntent()
    // 嵌入通用导航Intent
    data class CommonNavigation(val intent: CommonNavigationIntent) : SettingIntent()
}

sealed class SearchIntents {
    data class ChangeText(val text: String) : SearchIntents()
    object GetData : SearchIntents()
    object OpenFilters : SearchIntents()
    // ...其他页面专属Intent
    // 嵌入通用导航Intent
    data class CommonNavigation(val intent: CommonNavigationIntent) : SearchIntents()
}

处理的时候,在页面ViewModel里先判断是不是CommonNavigation,再转发到通用处理器:

// 可以把通用导航的处理逻辑抽成一个工具类或者BaseViewModel的方法
private fun handleCommonNavigation(intent: CommonNavigationIntent) {
    when (intent) {
        CommonNavigationIntent.NavigateToBack -> navController.popBackStack()
        is CommonNavigationIntent.NavigateToUserInfo -> navController.navigate("user_info/${intent.id}")
    }
}

// 页面ViewModel的Intent处理方法
fun handleIntent(intent: SearchIntents) {
    when (intent) {
        is SearchIntents.CommonNavigation -> handleCommonNavigation(intent.intent)
        is SearchIntents.ChangeText -> { /* 处理搜索文本 */ }
        // ...其他页面专属逻辑
    }
}

这种方式的优势是耦合度更低,页面可以自由选择是否引入通用Intent,适合大型项目或者需要更灵活Intent结构的场景。

方案三:封装通用Intent处理器(进阶复用)

如果不仅想复用Intent的定义,还想复用Intent的处理逻辑(比如导航的具体实现),可以封装一个BaseViewModel,把通用Intent的处理逻辑放在里面,页面ViewModel继承这个基类就行。

代码示例:

先定义BaseViewModel:

abstract class BaseViewModel(protected val navController: NavController) : ViewModel() {
    // 通用Intent处理方法
    protected open fun handleCommonIntent(intent: CommonIntent) {
        when (intent) {
            CommonIntent.NavigateToBack -> navController.popBackStack()
            is CommonIntent.NavigateToUserInfo -> navController.navigate("user_info/${intent.id}")
        }
    }

    // 每个页面ViewModel必须实现自己的专属Intent处理
    abstract fun handleScreenIntent(intent: Any)
}

然后页面ViewModel继承BaseViewModel:

class SearchViewModel(navController: NavController) : BaseViewModel(navController) {
    override fun handleScreenIntent(intent: Any) {
        when (intent) {
            is SearchIntents.ChangeText -> { /* 处理搜索文本 */ }
            is SearchIntents.GetData -> { /* 获取数据 */ }
            // 通用Intent直接交给父类处理
            is CommonIntent -> handleCommonIntent(intent)
            // ...其他页面专属逻辑
        }
    }
}

这种方式可以把通用逻辑的实现代码也完全复用,不用每个页面ViewModel都写一遍导航的具体代码,后期修改通用逻辑的时候只需要改BaseViewModel就行,维护成本极低,我现在在大型项目里就是用的这种方式。

实践小建议

  • 如果项目规模小(页面少于10个),方案一最省心,快速解决重复问题;
  • 如果项目规模大,或者需要更灵活的Intent结构,方案二或三更合适;
  • 不管用哪种方案,通用Intent的命名要尽量清晰,比如CommonNavigationIntentCommonIntent更明确,避免后期混淆;
  • 可以把通用Intent的处理逻辑(比如导航)抽成独立的NavigationHandler单例,这样不仅ViewModel可以用,其他地方也能复用。

希望这些方案能帮到你,要是有更复杂的场景,咱们再一起讨论~

火山引擎 最新活动