创建高级 widget

尝试使用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何使用 Compose 样式的 API 构建微件。

本页介绍了创建更高级的微件以提供更好用户体验的推荐做法。

更新微件内容的优化

更新微件内容可能需要大量计算资源。为了节省电池消耗,请优化更新类型、频率和时间。

微件更新类型

更新微件的方式有三种:完全更新、部分更新,以及(对于集合微件)数据刷新。每种方式的计算成本和影响都不同。

下面介绍了每种更新类型,并提供了相应的代码段。

  • 完全更新:调用 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);

只要应用具有与相应 相同的 UID,您就可以从应用中的任何位置调用这些方法。AppWidgetProvider

确定更新微件的频率

微件会根据为 updatePeriodMillis 属性提供的值定期更新。微件可以响应用户互动、广播更新或两者兼而有之。

定期更新

您可以通过在 appwidget-provider XML 中为 AppWidgetProviderInfo.updatePeriodMillis 指定值来控制定期更新的频率。每次更新都会触发 AppWidgetProvider.onUpdate() 方法,您可以在其中放置用于更新微件的代码。但是,如果您的微件需要异步加载数据或更新时间超过 10 秒,请考虑使用下一部分中介绍的广播接收器更新替代方案,因为 10 秒后,系统会将BroadcastReceiver视为无响应。

updatePeriodMillis 不支持小于 30 分钟的值。但是,如果您想停用定期更新,可以指定 0。

您可以让用户在配置中调整更新频率。例如,他们可能希望股票行情自动收录器每 15 分钟更新一次,或者一天只更新 4 次。在这种情况下,请将 updatePeriodMillis 设置为 0,并改用 WorkManager

响应用户互动进行更新

以下是一些根据用户互动更新微件的推荐方法:

  • 从应用的 activity :直接调用 AppWidgetManager.updateAppWidget 以响应用户互动,例如用户的点按操作。

  • 从远程互动(例如通知或应用微件) :构建 PendingIntent,然后从调用的 ActivityBroadcastService 更新微件。您可以选择自己的优先级。例如,如果您为 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.getBroadcastIntent