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
相关产品推荐
相关产品推荐

