构建 widget 托管应用

Android 主屏幕(适用于大多数 Android 设备)允许用户嵌入应用 widget(或 widget)以快速访问内容。如果您要构建主屏幕替换或类似应用,还可以通过实现 AppWidgetHost 来允许用户嵌入 widget。大多数应用并不需要这样做,但如果您要创建自己的托管应用,则必须了解托管应用默示同意的合同义务。

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

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

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

  • 应用 widget ID:每个 widget 实例在绑定时都分配有一个唯一 ID。请参阅 bindAppWidgetIdIfAllowed() 以及后面的绑定 widget 部分。主机使用 allocateAppWidgetId() 获取唯一 ID。此 ID 在 widget 的整个生命周期内一直保留,直到从主机中删除为止。任何特定于主机的状态(例如 widget 的大小和位置)都必须由托管软件包保留并与应用 widget ID 相关联。

  • 应用 widget 托管视图:将 AppWidgetHostView 视为一个框架,每当 widget 需要显示时,都会封装到该帧中。每当宿主扩充 widget 时,widget 都会与 AppWidgetHostView 相关联。

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

绑定 widget

当用户向托管应用添加 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 指定多个配置设置。您可以从与 widget 提供程序关联的 AppWidgetProviderInfo 对象中检索这些配置选项(以下几部分对这些配置选项进行了更详细的介绍)。

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

  • 添加微件时,请按照前文所述分配微件 ID。从宿主中移除 widget 后,请调用 deleteAppWidgetId() 以取消分配 widget ID。

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

  • widget 会在 AppWidgetProviderInfo 元数据中指定默认宽度和高度。这些值在单元格中定义。从 Android 12 开始(如果指定了 targetCellWidthtargetCellHeight)或 dps(如果仅指定了 minWidthminHeight)。请参阅 widget 大小调整属性

    请确保 widget 的布局至少为如此数量的 dp。例如,许多主机会在网格中对齐图标和 widget。在这种情况下,默认情况下,托管应用会使用满足 minWidthminHeight 约束条件的最小数量的单元格来添加 widget。

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

根据目标 Android 版本确定您的方法

Android 12

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

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

Android 12 还在 dps 中引入了 maxResizeWidthmaxResizeHeight 属性。我们建议,至少使用其中一个属性的 widget 不要超过这些属性指定的大小。

其他资源

  • 请参阅 Glance 参考文档。