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

如何实现接收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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:45:37