You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MVVM架构下全应用数据共享方案问询(含Hilt、网络状态监听场景)

全应用共享ViewModel关联数据的实现方案

核心原则

首先明确两个错误方案不可取:

  • 不要将全局共享状态存在Activity的公共var中,既没有可观察能力,也有极高的内存泄漏风险,不符合单向数据流规范
  • 不要强行将Activity级ViewModel共享给所有子页面,会导致严重的耦合问题,Compose场景下默认不会出现一个页面对应两个ViewModel的问题:viewModel()默认和当前导航栈条目生命周期绑定,如果你需要获取Activity级ViewModel,只需要传参viewModelStoreOwner = LocalContext.current as ComponentActivity即可,不会生成新的实例

基础实现方案(无DI框架场景)

  • 网络状态这类全局通用状态,优先封装成生命周期和应用一致的单例状态持有者,独立于Activity、ViewModel存在
  • 如果不想做单例,也可以将网络状态存在Activity级的MainViewModel的StateFlow/LiveData属性中,子页面ViewModel需要时直接传入该状态对象即可,不需要持有整个MainViewModel实例

进阶方案(Hilt + StateFlow 场景,适配你提供的项目结构)

思路:用Hilt注入一个单例的网络状态监视器,所有需要网络状态的ViewModel都直接注入这个实例订阅状态即可,完全不需要和Activity、其他ViewModel耦合,代码侵入性极低。

第一步:定义单例网络状态监视器

// 网络状态枚举
enum class NetworkState {
    AVAILABLE, UNAVAILABLE
}

@Singleton
class NetworkMonitor @Inject constructor(
    @ApplicationContext context: Context
) {
    private val connectivityManager = context.getSystemService(ConnectivityManager::class.java)
    
    private val _networkState = MutableStateFlow(NetworkState.AVAILABLE)
    val networkState: StateFlow<NetworkState> = _networkState.asStateFlow()
    
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            _networkState.update { NetworkState.AVAILABLE }
        }

        override fun onLost(network: Network) {
            _networkState.update { NetworkState.UNAVAILABLE }
        }
    }
    
    init {
        // 初始化时自动注册回调,和应用生命周期绑定,不需要在Activity中注册
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.registerDefaultNetworkCallback(networkCallback)
        } else {
            val request = NetworkRequest.Builder().build()
            connectivityManager.registerNetworkCallback(request, networkCallback)
        }
    }
    
    // 可选:应用退出时调用解除注册
    fun release() {
        runCatching { connectivityManager.unregisterNetworkCallback(networkCallback) }
    }
}

第二步:在业务ViewModel中注入使用

比如你的BottomSheetViewModel可以直接组合网络状态和业务请求状态:

@HiltViewModel
class BottomSheetViewModel @Inject constructor(
    networkMonitor: NetworkMonitor,
    // 其他业务依赖,比如仓库层实例
) : ViewModel() {
    // 组合网络状态和业务请求状态
    val state: StateFlow<RequestState> = combine(
        networkMonitor.networkState,
        // 你的业务请求返回的Flow
        businessRequestFlow
    ) { networkState, businessState ->
        if (networkState == NetworkState.UNAVAILABLE) {
            RequestState.NO_CONNECTION
        } else {
            businessState
        }
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = RequestState.Loading
    )
}

// 你的业务请求状态枚举示例
enum class RequestState {
    Loading, Success, Error, NO_CONNECTION
}

第三步:简化Activity侧代码

不需要在Activity中处理任何网络状态相关逻辑:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 不需要在这里注册网络回调、更新ViewModel状态
        setContent {
            NavHost(...) {
                composable("bottom_sheet") {
                    // 直接获取Hilt注入的ViewModel即可,内部已经自动注入了NetworkMonitor
                    val bottomSheetViewModel = viewModel<BottomSheetViewModel>()
                    BottomSheet(
                        state = bottomSheetViewModel.state.collectAsStateWithLifecycle().value
                    )
                }
            }
        }
    }
}

额外说明

  • 如果需要使用LiveData替换StateFlow,只需要把NetworkMonitor里的状态容器换成MutableLiveData,ViewModel侧用MediatorLiveData或者Transformations.switchMap组合状态即可,逻辑完全一致
  • 该方案下所有ViewModel都可以独立订阅全局网络状态,不需要互相持有、也不需要依赖宿主组件,完全解耦
  • 如果你需要共享其他全应用范围的状态,都可以参考这个思路,封装成单例的状态持有者注入使用即可

内容的提问来源于stack exchange,提问作者Lorenzo Benevento

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 23:18:01