本页介绍了创建更高级 widget 以提供更佳用户体验的推荐做法。
更新 widget 内容的优化措施
更新 widget 内容可能会耗费大量计算资源。为了节省电池消耗,请优化更新类型、频率和时间。
微件更新类型
更新 widget 的方式有三种:完整更新、部分更新,以及数据刷新(如果是集合 widget)。每种方法都有不同的计算成本和影响。
下面介绍了每种更新类型,并提供了每种类型的代码段。
- 完整更新:调用
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)以完整更新 widget。这会将之前提供的RemoteViews替换为新的RemoteViews。这是计算成本最高的更新。
val appWidgetManager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.packageName, R.layout.widgetlayout).apply { setTextViewText(R.id.textview_widget_layout1, "Updated text1") setTextViewText(R.id.textview_widget_layout2, "Updated text2") } appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
- 部分更新:调用
AppWidgetManager.partiallyUpdateAppWidget可更新 widget 的部分内容。这会将新的RemoteViews与之前提供的RemoteViews合并。如果微件未通过updateAppWidget(int[], RemoteViews)收到至少一次完整更新,系统会忽略此方法。
val appWidgetManager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.packageName, R.layout.widgetlayout).apply { setTextViewText(R.id.textview_widget_layout, "Updated text") } appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)
- 集合数据刷新:调用
AppWidgetManager.notifyAppWidgetViewDataChanged以使 widget 中集合视图的数据失效。这会触发RemoteViewsFactory.onDataSetChanged。在此期间,微件中会显示旧数据。您可以使用此方法安全地同步执行开销较大的任务。
val appWidgetManager = AppWidgetManager.getInstance(context) appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
只要应用与相应的 AppWidgetProvider 类具有相同的 UID,您就可以从应用中的任何位置调用这些方法。
确定更新 widget 的频率
微件会根据为 updatePeriodMillis 属性提供的值定期更新。该 widget 可以更新以响应用户互动、广播更新或同时响应这两者。
定期更新
您可以通过在 appwidget-provider XML 中为 AppWidgetProviderInfo.updatePeriodMillis 指定值来控制定期更新的频率。每次更新都会触发 AppWidgetProvider.onUpdate() 方法,您可以在其中放置用于更新 widget 的代码。不过,如果您的 widget 需要异步加载数据或需要超过 10 秒的时间来更新,请考虑下文所述的广播接收器更新替代方案,因为 10 秒后,系统会认为 BroadcastReceiver 无响应。
updatePeriodMillis 不支持小于 30 分钟的值。但是,如果您想停用定期更新,可以指定 0。
您可以让用户调整配置中的更新频率。例如,他们可能希望股票行情自动收录器每 15 分钟更新一次,或者每天仅更新 4 次。在这种情况下,请将 updatePeriodMillis 设置为 0,并改用 WorkManager。
更新以响应用户互动
以下是一些根据用户互动更新 widget 的推荐方法:
从应用的 activity 中:直接调用
AppWidgetManager.updateAppWidget以响应用户互动,例如用户点按。通过远程互动(例如通知或应用 widget):构建
PendingIntent,然后通过调用的Activity、Broadcast或Service更新 widget。您可以自行选择优先级。例如,如果您为PendingIntent选择Broadcast,则可以选择前台广播,以向BroadcastReceiver授予优先级。
更新以响应广播事件
例如,当用户拍照时,系统会发出需要更新 widget 的广播事件。在这种情况下,您希望在检测到新照片时更新 widget。
您可以使用 WorkManager 安排工作,并使用 Constraints.Builder.addContentUriTrigger 方法指定内容 URI 触发器。
您还可以为广播注册 BroadcastReceiver,例如监听 ACTION_LOCALE_CHANGED。
不过,由于这会消耗设备资源,因此请谨慎使用,并且仅监听特定广播。随着 Android 7.0(API 级别 24)和 Android 8.0(API 级别 26)中广播限制的引入,应用无法在其清单中注册隐式广播,但存在某些例外情况。
从 BroadcastReceiver 更新 widget 时的注意事项
如果微件是通过 BroadcastReceiver(包括 AppWidgetProvider)更新的,请注意以下有关微件更新时长和优先级的注意事项。
更新时长
通常,系统允许广播接收器(通常在应用的主线程中运行)运行最多 10 秒,然后才会将其视为无响应并触发应用无响应 (ANR) 错误。为避免在处理广播时阻塞主线程,请使用 goAsync 方法。如果更新 widget 需要更长时间,请考虑使用 WorkManager 安排任务。
Caution: Any work you do here blocks further broadcasts until it completes,
so it can slow the receiving of later events.
如需了解详情,请参阅安全注意事项和最佳实践。
更新的优先级
默认情况下,广播(包括使用 AppWidgetProvider.onUpdate 发出的广播)作为后台进程运行。这意味着,系统资源过载可能会导致广播接收器的调用延迟。如需优先处理广播,请将其设为前台进程。
例如,当用户点按 widget 的特定部分时,将 Intent.FLAG_RECEIVER_FOREGROUND 标志添加到传递给 PendingIntent.getBroadcast 的 Intent。