用途
本技能面向 Android 平台,讲解如何让主屏幕小组件渲染应用已有的状态持有者,而不是复制一份并行状态。核心思路是:小组件不是第二个应用,而是同一状态的第二个渲染器,自身作为依赖注入组件,注入与主应用界面相同的共享状态对象。
主要能力与做法
状态复用:小组件继承 GlanceAppWidget 并实现 DiComponent,通过 inject() 获取与主应用相同的状态持有者(如 PlayerStateHolder)和长生命周期协程作用域。小组件读取的是应用界面读取的同一组 Flow,因此不会与主应用对当前播放内容产生分歧;按钮直接派发应用自身的事件(如 playerStateHolder.onUIEvent(UIEvent.PlayPause)),无需维护小组件专用分支。AppWidgetReceiver 保持一行代码,返回 MainAppWidget() 即可。
显式更新循环:小组件跨进程绘制,宿主只重绘推送给它的内容。因此在 content 块内收集 Flow 只能更新组合,必须对每个已放置实例显式调用 update()。示例通过 GlanceAppWidgetManager.getGlanceIds 遍历所有 glance id 调用 update,并由注入了长生命周期作用域(如播放服务的 scope)的协程收集 controllerState 与 nowPlayingScreenData,使用 collectLatest 触发更新。收集器不能放在组合作用域,否则每次更新后被销毁,小组件会冻结。
注意事项:注入单例并在 provide 阶段查询,避免在 content 块中查询数据(每次重绘都会重新触发);将更新周期设得足够长。整个小组件应作为点击目标,外层容器可点击、内部控件覆盖;在非 Activity 上下文中启动 Intent 必须加 FLAG_ACTIVITY_NEW_TASK,否则会被静默丢弃。只在需要打开屏幕时使用深链,仅改变状态的操作直接调用单例。从 provideGlance 启动收集器会泄漏,应在重新提供前取消,或由持有作用域的组件启动一次。小组件需读取像素的位图必须关闭硬件位图(allowHardware 设为 false)。图片以位图或资源传递,不能传 URL,需自行加载并等待结果,为 null 结果提供占位。派生背景色需单独触发更新(如 LaunchedEffect(bgColor))。圆形图标按钮应使用扁平字形而非自带填充圆盘的图标。按钮缺少 enabled 参数时,置灰仍会触发,应补充 enabled 控制着色与点击。两个 Flow 汇入一次更新会在切歌时产生突发更新,应在源头合并并做 distinctUntilChanged。
验证方式:从应用改状态观察小组件跟随;从小组件改状态观察应用跟随;冷启动点击标题、标签和空白区域;放置两个小组件确认都更新;关闭应用前台界面保留服务后切歌确认收集器存活;用 grep 检查 allowHardware;多次重新添加小组件并确认 updateWidget 日志不重复出现。
适用场景与限制
适用于 Android 上基于 Glance 构建需与主应用共享状态的主屏幕小组件,例如媒体播放控制类小组件。限制:仅限 Android;示例代码为适配后的片段,深链 scheme 为占位符;文档未提供具体版本要求、完整安装命令或收费信息。