如何实现接收GlanceStateDefinition与GlanceAppWidget参数的Glance Widget基类?
Glance Widget 基类
GenericAction错误修复指南 问题概述
创建GenericAction基类用于统一处理Glance Widget更新,该类接收GlanceStateDefinition对象和GlanceAppWidget子类,分别用于状态更新和Widget刷新。目前遇到两个问题:
- 编译错误:
updateAppWidgetState的lambda中state.copy()提示Unresolved reference: copy,编译器无法推断泛型T的类型; - 运行时错误:实例化
glanceWidget时抛出NoSuchMethodException,找不到WidgetSymbolClass的带Context参数的构造方法。
相关代码片段
GenericAction基类
abstract class GenericAction<T>(val myObject: GlanceStateDefinition<T>, val myClass: Class<out WidgetSymbolClass>) : ActionCallback { override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) { val index = parameters.get(ActionParameters.Key<Int>("IndexParam")) ?: 0 val portfolioId = parameters.get(ActionParameters.Key<String>("PortfolioId")) ?: "Portfolio 1" val showPortfolioMenu = parameters.get(ActionParameters.Key<Boolean>("showMenu")) ?: false updateAppWidgetState(context, myObject, glanceId) { state -> state.copy( /*错误:无法解析引用:copy*/ /*错误:类型不匹配。需要:T 找到:Unit*/ showPortfolioMenu = showPortfolioMenu, ) } val glanceWidget = myClass.getConstructor(Context::class.java).newInstance(context) /*错误:java.lang.NoSuchMethodException WidgetSymbolClass.<init>*/ glanceWidget.updateStart( glanceId = glanceId, portfolioId = portfolioId, index = index) } }
子类实现
class WidgetSymbolShowMenu: GenericAction<WidgetSymbolState>(WidgetSymbolStateDefinition, WidgetSymbolClass::class.java)
Widget类定义
class WidgetSymbolClass @JvmOverloads constructor() : WidgetAbstractClass() abstract class WidgetAbstractClass (): GlanceAppWidget() { fun updateStart( glanceId: GlanceId?, appWidgetId: Int? = null, index: Int = 0, portfolioId: String = "Portfolio 1" ) { /*开始更新Widget*/ } }
状态定义与数据类
// WidgetSymbolState的状态定义 object StateDefinitionSymbol: GlanceStateDefinition<WidgetSymbolState> { override suspend fun getDataStore( context: Context, fileKey: String, ): DataStore<WidgetSymbolState> = context.widgetSymbolFile override fun getLocation(context: Context, fileKey: String): File { return context.dataStoreFile(widgetSymbolJson) } } // WidgetTableState的状态定义 object StateDefinitionTable: GlanceStateDefinition<WidgetTableState> { override suspend fun getDataStore( context: Context, fileKey: String, ): DataStore<WidgetTableState> = context.widgetTableFile override fun getLocation(context: Context, fileKey: String): File { return context.dataStoreFile(widgetTableJson) } } // WidgetSymbolState数据类 @Serializable data class WidgetSymbolState( val index: Int = 0, val portfolio: String = "Portfolio 1", val portfolioSize: Int = 0, val showPortfolioMenu: Boolean = false, ) // WidgetTableState数据类 @Serializable data class WidgetTableState( val portfolioId: String = "", val showPortfolioMenu: Boolean = false, )
错误修复方案
1. 修复copy方法编译错误
原因
泛型T没有任何约束,编译器无法确定T是支持copy方法的data class(只有data class会自动生成copy方法)。
解决步骤
步骤1:定义通用状态接口
创建接口约束所有状态类必须实现更新showPortfolioMenu的copy方法:
interface UpdatableState<T> { fun copy(showPortfolioMenu: Boolean): T }
步骤2:让状态类实现接口
修改两个状态数据类,实现UpdatableState接口:
@Serializable data class WidgetSymbolState( val index: Int = 0, val portfolio: String = "Portfolio 1", val portfolioSize: Int = 0, val showPortfolioMenu: Boolean = false, ) : UpdatableState<WidgetSymbolState> { override fun copy(showPortfolioMenu: Boolean): WidgetSymbolState { return copy(showPortfolioMenu = showPortfolioMenu) } } @Serializable data class WidgetTableState( val portfolioId: String = "", val showPortfolioMenu: Boolean = false, ) : UpdatableState<WidgetTableState> { override fun copy(showPortfolioMenu: Boolean): WidgetTableState { return copy(showPortfolioMenu = showPortfolioMenu) } }
步骤3:更新GenericAction泛型约束
将GenericAction的泛型T约束为UpdatableState<T>,让编译器识别copy方法:
abstract class GenericAction<T : UpdatableState<T>>(val myObject: GlanceStateDefinition<T>, val myClass: Class<out WidgetAbstractClass>) : ActionCallback { // ... 其他代码 }
2. 修复构造方法找不到的运行时错误
原因
WidgetSymbolClass的构造方法是无参的,但代码中尝试获取带Context参数的构造方法,导致反射调用失败。
解决步骤
根据需求选择以下两种方案之一:
方案A:使用无参构造实例化
直接修改实例化代码,调用无参构造:
// 替换原实例化代码 val glanceWidget = myClass.getConstructor().newInstance()
方案B:修改Widget类支持带Context的构造
如果Widget需要Context,调整基类和子类的构造方法:
// 修改WidgetAbstractClass abstract class WidgetAbstractClass(context: Context) : GlanceAppWidget(context) { fun updateStart( glanceId: GlanceId?, appWidgetId: Int? = null, index: Int = 0, portfolioId: String = "Portfolio 1" ) { /*开始更新Widget*/ } } // 修改WidgetSymbolClass class WidgetSymbolClass @JvmOverloads constructor(context: Context) : WidgetAbstractClass(context)
此时保留原实例化代码即可:
val glanceWidget = myClass.getConstructor(Context::class.java).newInstance(context)
修复后的完整GenericAction代码
abstract class GenericAction<T : UpdatableState<T>>(val myObject: GlanceStateDefinition<T>, val myClass: Class<out WidgetAbstractClass>) : ActionCallback { override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) { val index = parameters.get(ActionParameters.Key<Int>("IndexParam")) ?: 0 val portfolioId = parameters.get(ActionParameters.Key<String>("PortfolioId")) ?: "Portfolio 1" val showPortfolioMenu = parameters.get(ActionParameters.Key<Boolean>("showMenu")) ?: false updateAppWidgetState(context, myObject, glanceId) { state -> state.copy(showPortfolioMenu = showPortfolioMenu) } val glanceWidget = myClass.getConstructor().newInstance() glanceWidget.updateStart(glanceId = glanceId, portfolioId = portfolioId, index = index) } }
内容的提问来源于stack exchange,提问作者mars8
相关产品推荐
相关产品推荐

