如何向Android GlanceWorker注入运行时才确定的参数?
向GlanceWorker传递运行时动态参数的解决方案
核心思路
因为myObject(GlanceStateDefinition<T>)和myClass(Class<out WidgetAbstractClass>)是根据触发回调的小部件动态确定的,没法提前注入到WorkerFactory,所以采用传递类名字符串到WorkRequest输入数据,再在Worker内部反射还原实例的方案,适配运行时参数需求。
具体实现
1. 修改Action类,传递类名参数
在构建OneTimeWorkRequest时,把myObject和myClass的类名加入输入数据:
abstract class Action<T : ShowPortfolioMenuState<T>>( val myObject: GlanceStateDefinition<T>, val myClass: Class<out WidgetAbstractClass>, ) : ActionCallback { override suspend fun onAction( context: Context, glanceId: GlanceId, parameters: ActionParameters, ) { val glanceIdInt = GlanceAppWidgetManager(context).getAppWidgetId(glanceId) val showMenu = parameters[ActionParameters.Key("showMenu")] ?: false val glanceWork = OneTimeWorkRequest.Builder(GlanceWorkerPortfolioChange::class.java) .setInputData( workDataOf( "GLANCE_ID" to glanceIdInt, "SHOW_MENU" to showMenu, // 传递GlanceStateDefinition的类名 "STATE_DEFINITION_CLASS" to myObject.javaClass.name, // 传递Widget类的类名 "WIDGET_CLASS" to myClass.name ) ).build() WorkManager.getInstance(context) .beginUniqueWork("Worker Name", ExistingWorkPolicy.REPLACE, glanceWork ).enqueue() } }
2. 调整GlanceWorker,解析参数并执行逻辑
移除构造器中无法注入的参数,改为在doWork里通过反射还原实例,处理泛型擦除问题:
@HiltWorker class GlanceWorker @AssistedInject constructor( @Assisted val context: Context, @Assisted workerParameters: WorkerParameters, ): CoroutineWorker(context, workerParameters) { override suspend fun doWork(): Result { val glanceIdInt = inputData.getInt("GLANCE_ID", -1) val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(glanceIdInt) val showMenu = inputData.getBoolean("SHOW_MENU", false) // 读取输入的类名字符串,为空则直接返回失败 val stateDefClassName = inputData.getString("STATE_DEFINITION_CLASS") ?: return Result.failure() val widgetClassName = inputData.getString("WIDGET_CLASS") ?: return Result.failure() return try { // 加载GlanceStateDefinition类并获取实例(如果是单例,用getField("INSTANCE").get(null)更高效) val stateDefClass = Class.forName(stateDefClassName) as Class<GlanceStateDefinition<*>> val myObject = stateDefClass.getDeclaredConstructor().newInstance() as GlanceStateDefinition<ShowMenuState<*>> // 加载Widget类 val myClass = Class.forName(widgetClassName) as Class<out WidgetAbstractClass> // 执行小部件更新逻辑 myClass.apply { @Suppress("UNCHECKED_CAST") updateAppWidgetState(context, myObject as GlanceStateDefinition<ShowMenuState<Any>>, glanceId) { state -> (state as ShowMenuState<*>).updateShowMenu(showMenu) } update(context, glanceId) } Result.success() } catch (e: Exception) { e.printStackTrace() Result.failure() } } }
注意事项
- 如果你的
GlanceStateDefinition是单例实现(比如用object声明),可以把实例获取逻辑改成stateDefClass.getField("INSTANCE").get(null),避免反射调用构造方法的开销。 - 代码中的
@Suppress("UNCHECKED_CAST")是因为泛型信息在运行时被擦除,只要确保传递的类名对应正确的类型,这个转换就是安全的。 - 这种方案不需要自定义
WorkerFactory,完全依赖WorkRequest的输入数据传递动态参数,适配不同小部件的差异化需求。
内容的提问来源于stack exchange,提问作者mars8
相关产品推荐
相关产品推荐

