本页面介绍了针对 从而提供更好的用户体验
针对更新微件内容进行的优化
更新 widget 内容的计算开销可能会很大。节省电量 优化更新类型、频率和时间
widget 更新的类型
更新 widget 的方式有三种:完整更新、部分更新以及 对于集合微件,则是数据刷新。每种类型都有不同的 计算成本和影响。
下面介绍了每种更新类型并提供了每种更新类型的代码段。
完整更新:调用
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
完全更新该 widget。这会取代之前提供的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
来更新 widget 的某些部分。这会将新的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
使 widget 中集合视图的数据失效。这会触发RemoteViewsFactory.onDataSetChanged
。 在此期间,旧数据会显示在 widget 中。您可以安全地 与此方法同步执行开销大的任务。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
类。
确定微件的更新频率
微件会定期更新,具体取决于为
updatePeriodMillis
属性。该 widget 可以更新来响应用户互动、广播
更新,或者两者兼有。
定期更新
您可以通过指定
appwidget-provider
XML 中的 AppWidgetProviderInfo.updatePeriodMillis
。每个
update 会触发 AppWidgetProvider.onUpdate()
方法,该方法可让您
可以放置代码来更新 widget。不过,请考虑使用
广播接收器更新部分
如果您的微件需要异步加载数据,或者需要
因为在 10 秒后,系统会认为
BroadcastReceiver
设为无响应。
updatePeriodMillis
不支持小于 30 分钟的值。但是,如果
如果您想停用定期更新,可以将其指定为 0。
您可以允许用户在配置中调整更新频率。对于
例如,他们可能希望股票代码每 15 分钟更新一次,或者每 4 分钟更新一次
每天展示几次。在本例中,请将 updatePeriodMillis
设置为 0 并使用
改为 WorkManager
。
更新以响应用户互动
下面是一些根据用户互动更新 widget 的建议方法:
从应用的 activity:直接调用
AppWidgetManager.updateAppWidget
来响应用户互动,如 触发。通过远程互动(例如通知或应用微件): 构造一个
PendingIntent
,然后从调用的Activity
、Broadcast
或Service
。您可以自行选择优先级。对于 例如,如果您为PendingIntent
选择Broadcast
,则可以选择 一个前台广播BroadcastReceiver
优先级。
更新以响应广播活动
一个需要更新微件的广播事件示例是, 用户拍摄照片。在本例中,您想在上传新照片时更新微件 。
您可以使用 JobScheduler
安排作业,并指定广播作为
和
JobInfo.Builder.addTriggerContentUri
方法。
您也可以为广播注册 BroadcastReceiver
,例如,
监听
ACTION_LOCALE_CHANGED
。
不过,由于这会消耗设备资源,因此请谨慎使用并监听
只限于特定广播随着广播的引入
限制
7.0(API 级别 24)和 Android 8.0(API 级别 26),则应用无法进行隐式注册
广播,其中某些
例外情况。
从 BroadcastReceiver 更新 widget 时的注意事项
如果 widget 是从 BroadcastReceiver
更新的,包括
AppWidgetProvider
,请注意以下有关
widget 更新的持续时间和优先级。
更新的持续时间
一般来说,系统允许广播接收器,这些接收器通常在应用 主线程运行 10 秒后再将其视为无响应和 触发了 Application Not 响应 (ANR) 错误。如果超过这个时间 更新 widget,请考虑采用以下替代方案:
使用
WorkManager
安排任务。给接收者更多时间
goAsync
方法。 这样一来,接收器便可执行 30 秒。
请参阅安全注意事项和最佳 最佳做法 信息。
更新的优先级
默认情况下,广播(包括使用
AppWidgetProvider.onUpdate
- 作为后台进程运行。这意味着
过载的系统资源可能导致广播调用延迟
接收器。要确定广播的优先级,请将其设为前台进程。
例如,添加
Intent.FLAG_RECEIVER_FOREGROUND
将标记设置为 Intent
,并在用户执行该操作时传递给 PendingIntent.getBroadcast
。
。
构建包含动态项的准确预览
<ph type="x-smartling-placeholder">![](https://developer.android.com/static/images/appwidgets/missing-list.png?hl=zh-cn)
本部分介绍了在以下位置显示多个项目的推荐方法:
包含 集合的微件的微件预览
视图,也就是使用
ListView
、GridView
或 StackView
。
如果微件使用上述某个视图,则可通过直接创建可缩放的预览 为开发者提供 layout会降低 。出现这种情况的原因是 数据收集视图数据是在运行时动态设置的,类似于 如图 1 所示。
让包含集合视图的微件预览正确显示在微件中
我们建议您维护一个单独的布局文件,专门用于
预览。这个单独的布局文件包含实际的 widget 布局和一个
包含虚假商品的占位符集合视图。例如,您可以模仿
通过提供包含多个虚构列表的占位符 LinearLayout
来ListView
项。
为了举例说明 ListView
,请从单独的布局文件着手:
// res/layout/widget_preview.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/widget_background"
android:orientation="vertical">
// Include the actual widget layout that contains ListView.
<include
layout="@layout/widget_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// The number of fake items you include depends on the values you provide
// for minHeight or targetCellHeight in the AppWidgetProviderInfo
// definition.
<TextView android:text="@string/fake_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
<TextView android:text="@string/fake_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
</LinearLayout>
在提供以下内容的 previewLayout
属性时指定预览布局文件:
AppWidgetProviderInfo
元数据。您仍然需要指定实际的 widget 布局
为 initialLayout
属性指定样式,并在发生以下情况时使用实际的 widget 布局:
在运行时构建 RemoteViews
。
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
复杂列表项
上一部分中的示例提供了虚假列表项,因为列表
项是 TextView
对象。它可以
如果项的布局比较复杂,则提供虚假项。
假设有一个列表项是在 widget_list_item.xml
中定义的,并且包含
两个 TextView
对象:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_title" />
<TextView android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_content" />
</LinearLayout>
如需提供虚假列表项,您可以多次添加布局,但这 会导致每个列表项都完全相同。要提供唯一的列表项,请遵循 具体步骤:
为文本值创建一组属性:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
使用以下属性设置文本:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetTitle" /> <TextView android:id="@id/content" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetContent" /> </LinearLayout>
根据需要为预览创建任意数量的样式。重新定义 每种样式:
<resources> <style name="Theme.Widget.ListItem"> <item name="widgetTitle"></item> <item name="widgetContent"></item> </style> <style name="Theme.Widget.ListItem.Preview1"> <item name="widgetTitle">Fake Title 1</item> <item name="widgetContent">Fake content 1</item> </style> <style name="Theme.Widget.ListItem.Preview2"> <item name="widgetTitle">Fake title 2</item> <item name="widgetContent">Fake content 2</item> </style> </resources>
对预览布局中的虚构项目应用样式:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ...> <include layout="@layout/widget_view" ... /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview1" /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview2" /> </LinearLayout>