在功能块中显示定期更新

创建内容会随时间推移发生变化的功能块。

使用时间轴

时间轴由一个或多个 TimelineEntry 实例组成,每个实例中包含在特定间隔时间显示的布局。所有卡片都需要时间轴。

功能块时间轴图表

单条目卡片

通常,可以使用单个 TimelineEntry 来描述功能块。布局是固定不变的,只有布局中的信息会发生变化。例如,显示当天健身进度的功能块会始终显示相同的进度布局,但您可以调整该布局以显示不同的值。在此类情况下,您不会事先知道内容何时会发生变化。

下面是包含单个 TimelineEntry 的功能块示例:

Kotlin

override fun onTileRequest(
    requestParams: TileRequest
): ListenableFuture<Tile> {
    val tile = Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)

        // We add a single timeline entry when our layout is fixed, and
        // we don't know in advance when its contents might change.
        .setTileTimeline(
            Timeline.fromLayoutElement(...)
        ).build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
   Tile tile = new Tile.Builder()
       .setResourcesVersion(RESOURCES_VERSION)
       
       // We add a single timeline entry when our layout is fixed, and
       // we don't know in advance when its contents might change.
       .setTileTimeline(
            Timeline.fromLayoutElement(...)
       ).build();
   return Futures.immediateFuture(tile);
}

有时限的时间轴条目

TimelineEntry 可以选择性定义一个有效期,以允许功能块在某个已知时间更改其布局,而无需应用推送新的功能块。

一个典型的例子就是日程功能块,其时间轴包含一系列即将进行的活动。每个即将进行的活动都包含一个有效期,用于指示何时显示相应活动。

Tiles API 支持有效期重叠,在这种情况下,剩余时间最短的屏幕就是当前所显示的屏幕。一次只能显示一个事件。

开发者可以提供默认后备条目。例如,日程功能块可将一个具有无限有效期的功能块作为后备,如果没有其他有效的时间轴条目,就使用该功能块,如以下代码示例所示:

Kotlin

public override fun onTileRequest(
    requestParams: TileRequest
): ListenableFuture<Tile> {
    val timeline = Timeline.Builder()

    // Add fallback "no meetings" entry
    // Use the version of TimelineEntry that's in androidx.wear.protolayout.
    timeline.addTimelineEntry(TimelineEntry.Builder()
        .setLayout(getNoMeetingsLayout())
        .build()
    )

    // Retrieve a list of scheduled meetings
    val meetings = MeetingsRepo.getMeetings()
    // Add a timeline entry for each meeting
    meetings.forEach { meeting ->
        timeline.addTimelineEntry(TimelineEntry.Builder()
            .setLayout(getMeetingLayout(meeting))
            .setValidity(
                // The tile should disappear when the meeting begins
                // Use the version of TimeInterval that's in
                // androidx.wear.protolayout.
                TimeInterval.Builder()
                    .setEndMillis(meeting.dateTimeMillis).build()
            ).build()
        )
    }

    val tile = Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(timeline.build())
        .build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull RequestBuilders.TileRequest requestParams
) {
   Timeline.Builder timeline = new Timeline.Builder();
   // Add fallback "no meetings" entry
   // Use the version of TimelineEntry that's in androidx.wear.protolayout.
   timeline.addTimelineEntry(new TimelineEntry.Builder().setLayout(getNoMeetingsLayout()).build());
   // Retrieve a list of scheduled meetings
   List<Meeting> meetings = MeetingsRepo.getMeetings();
   // Add a timeline entry for each meeting
   for(Meeting meeting : meetings) {
        timeline.addTimelineEntry(new TimelineEntry.Builder()
            .setLayout(getMeetingLayout(meeting))
            .setValidity(
                // The tile should disappear when the meeting begins
                // Use the version of TimeInterval that's in
                // androidx.wear.protolayout.
                new TimeInterval.builder()
                    .setEndMillis(meeting.getDateTimeMillis()).build()
            ).build()
        );
    }

    Tile tile = new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(timeline.build())
        .build();
    return Futures.immediateFuture(tile);
}

刷新功能块

功能块上显示的信息可能会在一段时间后过期。例如,天气功能块如果全天显示相同的温度并不准确。

如需处理过期数据,请在创建功能块时设置新鲜度间隔时间,用于指定功能块的有效时长。在天气功能块示例中,您可能每小时更新一次内容,如以下代码示例所示:

Kotlin

override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
    Futures.immediateFuture(Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTileTimeline(Timeline.fromLayoutElement(
            getWeatherLayout())
        ).build()
    )

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
    return Futures.immediateFuture(new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTimeline(Timeline.fromLayoutElement(
            getWeatherLayout())
        ).build());
}

设置新鲜度间隔时间后,系统会在间隔时间结束后立即调用 onTileRequest()。如果未设置新鲜度间隔时间,则系统不会调用 onTileRequest()

功能块还可能因外部事件而过期。例如,用户可能会从自己的日历中移除某次会议;如果相应的功能块未刷新,那么该功能块仍会显示已删除的会议。在这种情况下,可以从应用代码中的任意位置请求刷新,如以下代码示例所示:

Kotlin

fun eventDeletedCallback() {
     TileService.getUpdater(context)
             .requestUpdate(MyTileService::class.java)
}

Java

public void eventDeletedCallback() {
   TileService.getUpdater(context)
           .requestUpdate(MyTileService.class);
}

选择更新工作流

您可以参考这些最佳实践来确定如何配置功能块更新:

  • 如果是可预测的更新(例如,如果是用户日历中的下一个活动),请使用时间轴。
  • 提取平台数据时,请使用数据绑定,以便系统自动更新数据。
  • 如果可在短时间内在设备上计算出更新(例如在日出功能块中更新图片的位置),请使用 onTileRequest()

    如果您需要提前生成所有图片,这种做法尤其有用。如果您需要在未来生成新图片,请调用 setFreshnessIntervalMillis()

  • 如果您反复执行更密集的后台工作(例如轮询天气数据),请使用 WorkManager 并将更新推送到功能块。

  • 如果更新是对外部事件(例如开灯、接收电子邮件或更新备注)的响应,请发送 Firebase Cloud Messaging (FCM) 消息,让应用恢复活跃状态,然后将更新推送到功能块。

  • 如果功能块数据同步过程占用大量资源,请执行以下操作:

    1. 安排数据同步。
    2. 启动 1-2 秒的定时器。
    3. 如果您在时间耗尽之前从远程数据源收到更新,则显示数据同步中已更新的值。否则,显示已缓存的本地值。