如何基于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
相关产品推荐
相关产品推荐

