构建 widget 托管应用

大多数 Android 设备上的 Android 主屏幕都允许用户嵌入应用 widget(或 widget),以便快速访问内容。如果您要构建主屏幕替代应用或类似的应用,还可以通过实现 AppWidgetHost 让用户嵌入 widget。大多数应用都不需要这样做,但如果您要创建自己的托管应用,请务必了解托管应用默许的约定义务。

本页面重点介绍实现自定义 AppWidgetHost 所涉及的责任。如需查看有关如何实现 AppWidgetHost 的具体示例,请参阅 Android 主屏幕 LauncherAppWidgetHost 的源代码。

下面简要介绍了实现自定义 AppWidgetHost 所涉及的关键类和概念:

  • 应用微件宿主AppWidgetHost 可为在界面中嵌入 widget 的应用提供与 AppWidget 服务的互动。AppWidgetHost 必须具有一个在托管应用自己的软件包中独一无二的 ID。此 ID 在托管应用的所有使用场合中保持不变。此 ID 通常是您在应用中分配的硬编码值。

  • 应用微件 ID:每个微件实例在绑定时都会分配有一个唯一 ID。请参阅 bindAppWidgetIdIfAllowed(),如需了解详情,请参阅下文中的绑定 widget 部分。宿主使用 allocateAppWidgetId() 获取唯一 ID。 此 ID 在微件的整个生命周期内(也就是说,直到从托管应用中将其删除)保持不变。任何特定于托管应用的状态(如微件的大小和位置)都必须由托管软件包保留并与应用微件 ID 关联。

  • 应用微件宿主视图:可以将 AppWidgetHostView 看作一个框架,每当微件需要显示时,都会封装到该框架中。每当 widget 由宿主扩充时,widget 都会与 AppWidgetHostView 相关联。

    • 默认情况下,系统会创建 AppWidgetHostView,但主机可以通过扩展 AppWidgetHostView 来创建自己的 AppWidgetHostView 子类。
    • 从 Android 12(API 级别 31)开始,AppWidgetHostView 引入了 setColorResources()resetColorResources() 方法来处理动态过载的颜色。宿主负责向这些方法提供颜色。
  • 选项包AppWidgetHost 使用选项包将有关如何显示 widget 的信息(例如大小范围列表)以及 widget 是在锁定屏幕上还是在主屏幕上,传达给 AppWidgetProvider。利用此信息,AppWidgetProvider 可以根据微件的显示方式和显示位置来量身定制微件的内容和外观。您可以使用 updateAppWidgetOptions()updateAppWidgetSize() 来修改 widget 的 bundle。这两种方法都会触发对 AppWidgetProvideronAppWidgetOptionsChanged() 回调。

绑定 widget

当用户向宿主添加 widget 时,会发生一个称为“绑定”的流程。绑定是指将特定应用微件 ID 与特定托管应用和特定 AppWidgetProvider 相关联。

绑定 API 还使托管应用能够提供用于绑定的自定义界面。如需使用此流程,您的应用必须在宿主的清单中声明 BIND_APPWIDGET 权限:

<uses-permission android:name="android.permission.BIND_APPWIDGET" />

但是,这只是第一步。在运行时,用户必须显式向您的应用授予权限,允许它将 widget 添加到宿主。如需测试您的应用是否具有添加 widget 的权限,请使用 bindAppWidgetIdIfAllowed() 方法。如果 bindAppWidgetIdIfAllowed() 返回 false,则您的应用必须显示一个对话框来提示用户授予权限:针对当前添加的 widget 选择“允许”,或针对将来添加的所有 widget 选择“始终允许”。

以下代码段举例说明了如何显示该对话框:

Kotlin

val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_BIND).apply {
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName)
    // This is the options bundle described in the preceding section.
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options)
}
startActivityForResult(intent, REQUEST_BIND_APPWIDGET)

Java

Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
// This is the options bundle described in the preceding section.
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);

宿主必须检查用户添加的 widget 是否需要配置。如需了解详情,请参阅允许用户配置应用 widget

托管应用的责任

您可以使用 AppWidgetProviderInfo 元数据为 widget 指定一些配置设置。您可以从与微件提供程序关联的 AppWidgetProviderInfo 对象检索这些配置选项(下文对此进行了更详细的说明)。

无论您的目标 Android 版本是哪个,所有托管应用都有以下责任:

  • 添加微件时,请按照前面的说明分配微件 ID。从宿主中移除 widget 时,请调用 deleteAppWidgetId() 以取消分配 widget ID。

  • 添加 widget 时,请检查是否需要启动配置 activity。通常,如果 widget 的配置 activity 存在且未通过同时指定 configuration_optionalreconfigurable 标志来标记为可选,则宿主需要启动该 activity。如需了解详情,请参阅通过配置 activity 更新 widget。对于许多 widget 来说,这是一个必要的步骤,执行此步骤后,才能显示这些 widget。

  • 微件在 AppWidgetProviderInfo 元数据中指定默认宽度和高度。这些值以 cell 为单位定义(从 Android 12 开始,如果指定了 targetCellWidthtargetCellHeight),或者以 dp 为单位定义(如果仅指定了 minWidthminHeight)。请参阅widget 大小调整属性

    请确保微件的布局中至少包含这么多 dp。例如,许多托管应用在网格中将图标与微件对齐。在这种情况下,托管应用默认应使用满足 minWidthminHeight 约束条件的最小数量的单元格来添加微件。

除了上一部分列出的要求之外,特定平台版本引入的一些功能还要求托管应用承担新的责任。

根据目标 Android 版本确定方法

Android 12

Android 12(API 级别 31)捆绑了一个额外的 List<SizeF>,其中包含微件实例可在选项 bundle 中采用的可能尺寸(以 dp 为单位)的列表。提供的尺寸数量取决于主机实现。主机通常会为手机提供两种尺寸(竖屏和横屏),为可折叠设备提供四种尺寸。

AppWidgetProvider 可向 RemoteViews 提供的不同 RemoteViews 数量上限为 MAX_INIT_VIEW_COUNT (16)。 由于 AppWidgetProvider 对象将 RemoteViews 对象映射到 List<SizeF> 中的每个尺寸,因此请勿提供超过 MAX_INIT_VIEW_COUNT 个尺寸。

Android 12 还引入了以 dp 为单位的 maxResizeWidthmaxResizeHeight 属性。我们建议,使用至少一个此类属性的小部件的大小不应超过相应属性指定的大小。

其他资源

  • 请参阅 Glance 参考文档。