在磁贴中显示定期更新

创建内容会随时间推移而变化的磁贴。

使用时间线

时间线由一个或多个 TimelineEntry 实例组成,每个实例都包含在特定时间间隔内显示的布局。所有磁贴都需要时间线。

Diagram of tile timeline

单条目磁贴

磁贴通常可以用单个 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);
}

基于时间的 Timeline 条目

TimelineEntry 可选地定义有效期,允许磁贴在已知时间更改其布局,而无需应用推送新的磁贴。

典型的示例是议程磁贴,其时间线包含一系列即将发生的事件。每个即将发生的事件都包含一个有效期,以指示何时显示它。

磁贴 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. 如果在计时器超时前从远程数据源收到更新,则显示来自数据同步的更新值。否则,显示缓存的本地值。