在信息卡中显示周期性更新

创建内容随时间变化的信息卡。

使用时间轴

时间轴由一个或多个 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);
}

有时限的时间轴条目

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