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

Android Glance Widget编程式添加时如何向Receiver传递额外参数?

编程式固定Android Widget时传递额外参数

问题背景

使用Kotlin 1.9.10和Glance 1.0.0开发Android Widget应用,当前已能通过onReceive获取刚固定的Widget ID,但需要在固定过程中传递额外值,以便将Widget ID与该值关联存入本地存储。

当前固定Widget的代码:

val appWidgetManager = AppWidgetManager.getInstance(context)
val myProvider = ComponentName(context, MyWidgetReceiver::class.java)

val pinnedWidgetCallbackIntent = Intent(Intent.ACTION_MAIN)
pinnedWidgetCallbackIntent.addCategory(Intent.CATEGORY_HOME)
pinnedWidgetCallbackIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

val successCallback = PendingIntent.getActivity(
        context, 0,
        pinnedWidgetCallbackIntent, PendingIntent.FLAG_IMMUTABLE
    )
    
appWidgetManager.requestPinAppWidget(myProvider, null, successCallback)

当前Receiver的onReceive处理:

override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)
        if (intent.action == AppWidgetManager.ACTION_APPWIDGET_UPDATE) {
            val extras = intent.extras
            val widgetId = extras?.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS)?.firstOrNull() ?: -1
        }
    }

解决方案

方案一:通过requestPinAppWidget的Options参数传递

利用requestPinAppWidget的第二个参数(options: Bundle?)传递额外值,系统会将该Bundle关联到固定后的Widget实例,后续可通过AppWidgetManager获取。

修改固定Widget的代码:

val appWidgetManager = AppWidgetManager.getInstance(context)
val myProvider = ComponentName(context, MyWidgetReceiver::class.java)

// 1. 构造携带额外值的Bundle
val widgetOptions = Bundle().apply {
    putString("EXTRA_CUSTOM_DATA", "你的自定义内容") // 可根据需求添加其他类型数据,如putInt、putBoolean
}

// 2. 保持原有的成功回调Intent(可根据需求调整)
val pinnedWidgetCallbackIntent = Intent(Intent.ACTION_MAIN).apply {
    addCategory(Intent.CATEGORY_HOME)
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
}

val successCallback = PendingIntent.getActivity(
    context,
    System.currentTimeMillis().toInt(), // 用时间戳作为requestCode,避免PendingIntent重复
    pinnedWidgetCallbackIntent,
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

// 3. 传递自定义options调用固定方法
appWidgetManager.requestPinAppWidget(myProvider, widgetOptions, successCallback)

修改Receiver的onReceive方法:

override fun onReceive(context: Context, intent: Intent) {
    super.onReceive(context, intent)
    
    when(intent.action) {
        // 监听Widget固定成功的广播(比ACTION_APPWIDGET_UPDATE更精准)
        AppWidgetManager.ACTION_APPWIDGET_PINNED -> {
            val widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
            if (widgetId != -1) {
                // 从Widget实例中获取之前传递的额外值
                val options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId)
                val customData = options.getString("EXTRA_CUSTOM_DATA")
                
                if (!customData.isNullOrEmpty()) {
                    // 将Widget ID与额外值关联存入本地存储
                    context.getSharedPreferences("WidgetStorage", Context.MODE_PRIVATE).edit()
                        .putString("widget_$widgetId", customData)
                        .apply()
                }
            }
        }
        AppWidgetManager.ACTION_APPWIDGET_UPDATE -> {
            // 保留原有的更新逻辑
            val widgetIds = intent.extras?.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS) ?: return
            val widgetId = widgetIds.firstOrNull() ?: -1
            // ...
        }
    }
}

方案二:结合SharedPreferences临时存储(兼容性更强)

部分Launcher可能不会完整传递options中的参数,此时可通过SharedPreferences临时存储额外值,再通过成功回调Activity完成关联。

修改固定Widget的代码:

val appWidgetManager = AppWidgetManager.getInstance(context)
val myProvider = ComponentName(context, MyWidgetReceiver::class.java)

// 1. 生成唯一临时键,用于关联额外值
val tempKey = "temp_widget_${System.currentTimeMillis()}"
val customData = "你的自定义内容"

// 2. 将额外值存入临时SharedPreferences
context.getSharedPreferences("WidgetTemp", Context.MODE_PRIVATE).edit()
    .putString(tempKey, customData)
    .apply()

// 3. 构造成功回调Intent,携带临时键
val successCallbackIntent = Intent(context, WidgetPinSuccessActivity::class.java).apply {
    putExtra("EXTRA_TEMP_KEY", tempKey)
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
}

val successCallback = PendingIntent.getActivity(
    context,
    tempKey.hashCode(), // 用临时键的哈希值作为requestCode,确保唯一
    successCallbackIntent,
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

// 4. 调用固定方法
appWidgetManager.requestPinAppWidget(myProvider, null, successCallback)

新增WidgetPinSuccessActivity处理关联:

class WidgetPinSuccessActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val tempKey = intent.getStringExtra("EXTRA_TEMP_KEY") ?: run {
            finish()
            return
        }
        
        // 获取该Provider下所有Widget ID,取最新固定的(假设最后一个为最新)
        val appWidgetManager = AppWidgetManager.getInstance(this)
        val widgetIds = appWidgetManager.getAppWidgetIds(ComponentName(this, MyWidgetReceiver::class.java))
        val latestWidgetId = widgetIds.lastOrNull() ?: run {
            finish()
            return
        }
        
        // 从临时存储中取出额外值
        val customData = getSharedPreferences("WidgetTemp", MODE_PRIVATE).getString(tempKey, null)
        
        if (!customData.isNullOrEmpty()) {
            // 关联存储到正式本地存储
            getSharedPreferences("WidgetStorage", MODE_PRIVATE).edit()
                .putString("widget_$latestWidgetId", customData)
                .apply()
            
            // 删除临时数据
            getSharedPreferences("WidgetTemp", MODE_PRIVATE).edit()
                .remove(tempKey)
                .apply()
        }
        
        finish() // 立即关闭Activity,用户无感知
    }
}

注意事项

  • ACTION_APPWIDGET_PINNED是Widget固定成功时系统发送的专属广播,比ACTION_APPWIDGET_UPDATE更精准对应固定动作
  • 使用PendingIntent时,务必使用唯一的requestCode(如时间戳、临时键哈希),避免PendingIntent被重复覆盖
  • 方案二兼容性更强,适用于部分对options参数支持不佳的Launcher

内容的提问来源于stack exchange,提问作者Dang Nguyen Hoang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 21:30:56