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