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

如何基于Clean架构、Hilt等优雅处理Jetpack Glance Widget状态?

优雅处理Jetpack Glance Widget状态的Clean架构方案

核心思路

在Clean架构+Hilt的应用中整合Glance Widget,核心是让Widget复用现有应用的数据流链路,避免单独维护一套状态逻辑。通过一个中间桥接层,打通Hilt管理的业务组件与Widget的状态通道,同时保持架构分层清晰。

具体实现步骤

1. 实现Widget数据桥接层

由于Glance Widget无法直接依赖Hilt注入的实例,我们需要一个单例的WidgetDataBridge作为中间层:它接收应用层的数据流,将数据缓存到Glance可访问的DataStore(比SharedPreferences更适配Flow场景),同时提供Widget可观察的数据流。

class WidgetDataBridge @Inject constructor(
    private val dataStore: DataStore<Preferences>,
    private val getWeatherUseCase: GetWeatherUseCase,
    private val context: Context
) {
    // 启动数据同步:监听UseCase输出,更新DataStore并触发Widget刷新
    fun startSync() {
        getWeatherUseCase()
            .onEach { weatherData ->
                // 序列化气象数据存入DataStore(推荐用ProtoBuf或Gson)
                dataStore.edit { preferences ->
                    preferences[WEATHER_DATA_KEY] = weatherData.toJson()
                }
                // 通知所有Widget实例刷新
                GlanceAppWidgetManager(context).getGlanceIds(WeatherGlanceWidget::class.java)
                    .forEach { glanceId ->
                        WeatherGlanceWidget().update(context, glanceId)
                    }
            }
            .launchIn(GlobalScope) // 可替换为应用级CoroutineScope
    }

    // 供Widget观察的数据流
    fun observeWeatherData(): Flow<WeatherData?> {
        return dataStore.data
            .map { preferences ->
                preferences[WEATHER_DATA_KEY]?.let { json ->
                    json.fromJson<WeatherData>()
                }
            }
    }

    companion object {
        // Hilt单例绑定,确保全局唯一
        @Provides
        @Singleton
        fun provideWidgetDataBridge(
            dataStore: DataStore<Preferences>,
            getWeatherUseCase: GetWeatherUseCase,
            @ApplicationContext context: Context
        ): WidgetDataBridge {
            val bridge = WidgetDataBridge(dataStore, getWeatherUseCase, context)
            bridge.startSync() // 初始化时启动同步
            return bridge
        }

        private val WEATHER_DATA_KEY = stringPreferencesKey("weather_data")
    }
}

2. Glance Widget接入数据流

通过Hilt的EntryPoint获取WidgetDataBridge实例,观察其数据流来渲染UI:

class WeatherGlanceWidget : GlanceAppWidget() {
    override suspend fun provideGlance(context: Context, id: GlanceId): GlanceStateDefinition<*> {
        return GlanceStateDefinition.None
    }

    override suspend fun compose(context: Context, id: GlanceId) {
        // 通过EntryPoint获取桥接层实例
        val bridge = EntryPointAccessors.fromApplication(
            context,
            WidgetDataBridgeEntryPoint::class.java
        ).widgetDataBridge()

        val weatherData = bridge.observeWeatherData().first()

        weatherData?.let { data ->
            Column(
                modifier = GlanceModifier.fillMaxSize().padding(16.dp),
                verticalAlignment = Alignment.CenterVertically,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text("当前温度: ${data.temp}°C", style = TextStyle(fontSize = 18.sp))
                Text("湿度: ${data.humidity}%", style = TextStyle(fontSize = 14.sp))
                // 其他气象数据展示组件
            }
        } ?: run {
            Text("加载中...")
        }
    }

    // 定义Hilt EntryPoint,用于获取桥接层实例
    @InstallIn(SingletonComponent::class)
    interface WidgetDataBridgeEntryPoint {
        fun widgetDataBridge(): WidgetDataBridge
    }
}

3. 保持架构一致性

  • UseCase/Repository层:无需修改,继续负责业务逻辑与数据获取,保持原有的Flow输出模式。
  • Hilt依赖管理:所有核心组件(UseCase、Repository、DataStore)仍由Hilt注入,WidgetDataBridge作为单例绑定,确保依赖可控且全局唯一。
  • 状态流转:数据从网络/本地缓存经UseCase→Repository→WidgetDataBridge→DataStore→Widget,完全复用应用现有数据流链路,避免状态冗余。

方案优势

  • 无重复状态维护:Widget与应用共享同一数据源,无需单独处理网络请求或本地存储逻辑。
  • 架构分层清晰:Widget仅负责UI渲染,数据逻辑完全复用Clean架构的现有流程,不打破分层规则。
  • 响应式更新:通过Flow实现数据变化时自动同步到Widget,无需手动编写复杂的刷新触发逻辑。

内容的提问来源于stack exchange,提问作者Hamidreza Sahraei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:58:13