# Android 实时更新

Pushwoosh 通过 `pushwoosh-liveupdates` 模块（SDK 6.9.0 及更高版本）支持 Android 实时更新。实时更新是一种持续进行的、进度条样式的通知，系统会将其提升到锁屏、通知抽屉中，并在状态栏中显示为状态图标，这样用户无需打开您的应用即可跟踪活动。

整个生命周期由服务器驱动：您的后端在活动开始时发送一个推送，随着活动进展发送更多推送，并在活动结束时发送最后一个推送。SDK 会自动渲染每一个推送。

## 什么是实时更新

实时更新是 Android 16 (API 36) 中引入的一项功能，用于从头到尾展示用户发起的、具有时效性的活动。它们建立在平台的以进度为中心的通知和 `Notification.ProgressStyle` API 之上。从概念上讲，它们是 Android 平台上与 iOS 实时活动相对应的功能。

本页面仅涵盖 Pushwoosh 集成。有关平台行为、提升规则和设计指南，请参阅官方 Android 文档：

- [以进度为中心的通知](https://developer.android.com/about/versions/16/features/progress-centric-notifications)
- [创建实时更新通知 (Views)](https://developer.android.com/develop/ui/views/notifications/live-update)
- [创建实时更新通知 (Compose)](https://developer.android.com/develop/ui/compose/notifications/live-update)

## 何时使用实时更新

实时更新适用于正在进行的、用户发起的、具有时效性的活动——即用户当前积极关心的、有明确开始和结束的事件。Pushwoosh 客户的典型场景包括：

- **食品配送** — 订单已接受、正在准备、派送中、即将送达。
- **网约车和出租车** — 已分配司机、司机在途中、即将到达、行程进行中。
- **订单和货运跟踪** — 正在运输中的订单的实时状态。
- **体育赛事和媒体直播** — 比赛进行中的比分和时间。
- **健身** — 正在进行的锻炼或跑步，显示已用时间和进度。
- **金融科技** — 正在经历各个阶段的交易或验证流程。

由于生命周期是由您的后端已经知道的真实事件（例如订单状态改变、快递员移动）驱动的，因此实时更新通常是在您现有事件流中连接的一个 API 调用，而不是由人工手动发送。

<Aside type="caution">
请勿将实时更新用于促销、聊天消息或环境信息，并且绝不要重新发布用户已关闭的实时更新。Android 可能会撤销应用发布提升通知的能力。请遵循 [Android 关于适当使用的指南](https://developer.android.com/develop/ui/views/notifications/live-update#best-practices)。
</Aside>

## 要求

- Android 16 (API 36) 或更高版本。在较旧的设备上，该模块保持非活动状态，每个实时更新 API 调用都是安全的无操作。
- Pushwoosh Android SDK 6.9.0 或更高版本。

## 添加 pushwoosh-liveupdates 模块

将依赖项添加到您的 **app/build.gradle** 文件中：

```groovy
dependencies {
    implementation 'com.pushwoosh:pushwoosh-liveupdates:<latest-version>'
}
```

请将 `<latest-version>` 替换为 [Maven Central](https://mvnrepository.com/artifact/com.pushwoosh/pushwoosh-liveupdates) 上的当前版本。

该模块在启动时会自动被发现。它声明了所需的 `POST_PROMOTED_NOTIFICATIONS` 权限，注册了自己的通知渠道，并在默认通知路径之前拦截实时更新推送。无需额外的初始化代码——SDK 会为您设置 ongoing 和 promoted 标志、下载大图标、映射操作按钮并发布通知。

## 发送实时更新

您可以通过 [Messaging API v2](/zh/developer/api-reference/messaging-api-v2/) 发送实时更新，方法是在 `android` 内容块中添加一个 `live_update` 对象。请使用 `transactional` 请求——实时更新针对的是其活动被跟踪的特定用户。`schedule` 字段是必需的；`{ "after": "0s" }` 表示立即发送。生命周期有三个操作，在 `live_update.op` 中设置：

- `OPERATION_START` — 活动的第一个推送。发布持续进行的通知。
- `OPERATION_UPDATE` — 同一活动的后续推送。在原地静默刷新它。
- `OPERATION_END` — 最终推送。关闭通知。

属于同一活动的所有推送必须共享相同的 `live_update.id`。该 ID 将更新联系在一起，也是您从应用中关闭更新时使用的 ID。

每个推送都完整地描述了通知——没有任何内容会从上一个推送继承。对于每次 `OPERATION_UPDATE`，请重新发送您希望保留的每个字段，例如分段和大图标；省略的字段将被渲染为不存在。

### 实时更新参数

这些键位于 `android` 内容块的 `live_update` 对象内部。标题、正文和大图标使用标准的 Android 推送字段（`title`、`body`、`custom_icon`），与 `live_update` 一同发送。

| 参数 | 类型 | 描述 |
|---|---|---|
| `op` | string | 生命周期操作：`OPERATION_START`、`OPERATION_UPDATE` 或 `OPERATION_END`。必需。 |
| `id` | string | 同一个实时更新的所有推送共享的稳定活动 ID。必需。 |
| `progress` | int | 进度值，相对于所有分段长度的总和来衡量。 |
| `progress_indeterminate` | bool | 显示不确定动画而不是具体值。 |
| `progress_bar` | bool | 是否显示进度条。默认为 `true`。 |
| `segments` | array | 有序的进度分段，每个分段为 `{ "color": "#RRGGBB", "length": N }`。 |
| `extras` | object | 传递给自定义样式提供程序的任意数据。 |
| `when` | int64 | 头部时间锚点，以纪元毫秒为单位。 |
| `chronometer` | bool | 将头部时间显示为运行中的计时器。 |
| `chronometer_count_down` | bool | 运行中的计时器倒计时而不是正计时。 |
| `show_when` | bool | 是否显示头部时间列。默认为 `true`。 |

<Aside>
`op` 的值必须是确切的枚举名称 `OPERATION_START`、`OPERATION_UPDATE` 或 `OPERATION_END` 之一——缩写形式将被忽略。每个字段都带有其原生的 JSON 类型：`segments` 是一个 JSON 数组，`extras` 是一个 JSON 对象，而不是编码后的字符串。
</Aside>

四个时间字段的组合如下：当 `show_when` 设置为 `false` 时，时间被隐藏；否则 `when` 是锚点，`chronometer` 将其变为实时计数器，而 `chronometer_count_down` 使该计数器倒计时。

### 开始推送

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "title": "Order #4521",
                "body": "We are preparing your order",
                "custom_icon": "https://example.com/restaurant.png",
                "live_update": {
                  "op": "OPERATION_START",
                  "id": "order_4521",
                  "progress": 1,
                  "segments": [
                    { "color": "#34A853", "length": 3 },
                    { "color": "#FBBC05", "length": 4 },
                    { "color": "#4285F4", "length": 3 }
                  ],
                  "extras": { "eta": "18:40" }
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

### 更新推送

每当活动向前推进时，发送一个带有相同 `id` 的 `OPERATION_UPDATE`。重复分段和图标——省略它们的更新将渲染为没有它们。

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "title": "Order #4521",
                "body": "Your courier is on the way",
                "custom_icon": "https://example.com/restaurant.png",
                "live_update": {
                  "op": "OPERATION_UPDATE",
                  "id": "order_4521",
                  "progress": 7,
                  "segments": [
                    { "color": "#34A853", "length": 3 },
                    { "color": "#FBBC05", "length": 4 },
                    { "color": "#4285F4", "length": 3 }
                  ]
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

### 结束推送

最终的推送只需要操作和 ID。

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "live_update": {
                  "op": "OPERATION_END",
                  "id": "order_4521"
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

## 自定义外观

SDK 提供了一个默认的进度条样式，该样式由 `progress`、`progress_indeterminate` 和 `segments` 构建。要完全控制进度条，请实现 `LiveUpdateProgressStyleProvider` 并自行构建一个 `Notification.ProgressStyle`。

该提供程序是唯一的自定义点。SDK 仍然负责渠道设置、ongoing 和 promoted 标志、大图标、操作按钮和头部时间——自定义提供程序只能塑造进度条，因此不会破坏提升资格。它必须是无状态的：仅从提供的 `LiveUpdateState` 派生返回的样式。如果它抛出异常，SDK 会回退到默认样式，通知仍然会发布。

<Tabs>
<TabItem label="Java">
```java
import android.app.Notification;
import androidx.annotation.NonNull;
import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider;
import com.pushwoosh.liveupdates.LiveUpdateSegment;
import com.pushwoosh.liveupdates.LiveUpdateState;
import java.util.List;

public class OrderStyleProvider implements LiveUpdateProgressStyleProvider {
    @NonNull
    @Override
    public Notification.ProgressStyle createStyle(@NonNull LiveUpdateState state) {
        Notification.ProgressStyle style = new Notification.ProgressStyle();
        if (state.getProgress() != null) {
            style.setProgress(state.getProgress());
        }
        style.setProgressIndeterminate(state.isProgressIndeterminate());

        List<LiveUpdateSegment> segments = state.getSegments();
        int boundary = 0;
        for (int i = 0; i < segments.size(); i++) {
            LiveUpdateSegment seg = segments.get(i);
            style.addProgressSegment(
                new Notification.ProgressStyle.Segment(seg.getLength()).setColor(seg.getColor()));
            boundary += seg.getLength();
            if (i < segments.size() - 1) {
                style.addProgressPoint(new Notification.ProgressStyle.Point(boundary));
            }
        }
        return style;
    }
}
```
</TabItem>
<TabItem label="Kotlin">
```kotlin
import android.app.Notification
import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider
import com.pushwoosh.liveupdates.LiveUpdateState

class OrderStyleProvider : LiveUpdateProgressStyleProvider {
    override fun createStyle(state: LiveUpdateState): Notification.ProgressStyle {
        val style = Notification.ProgressStyle()
        state.progress?.let { style.setProgress(it) }
        style.setProgressIndeterminate(state.isProgressIndeterminate)

        val segments = state.segments
        var boundary = 0
        segments.forEachIndexed { i, seg ->
            style.addProgressSegment(
                Notification.ProgressStyle.Segment(seg.length).setColor(seg.color))
            boundary += seg.length
            if (i < segments.size - 1) {
                style.addProgressPoint(Notification.ProgressStyle.Point(boundary))
            }
        }
        return style
    }
}
```
</TabItem>
</Tabs>

在 **AndroidManifest.xml** 中使用 `<meta-data>` 标签注册提供程序。该类必须有一个公共的无参数构造函数。

```xml
<meta-data
    android:name="com.pushwoosh.LIVE_UPDATE_STYLE_PROVIDER"
    android:value="com.example.OrderStyleProvider" />
```

使用 `LiveUpdateState.getExtras()` 来读取您在 `live_update.extras` 中发送的 JSON，并根据您自己的业务数据调整样式。

## 从您的应用管理实时更新

服务器驱动每个 `OPERATION_START`、`OPERATION_UPDATE` 和 `OPERATION_END`，因此没有应用端的 API 来发布或刷新实时更新。`PushwooshLiveUpdates` 接口仅涵盖服务器无法完成的操作——在本地关闭更新和检查哪些更新正在屏幕上显示。

```java
import com.pushwoosh.liveupdates.PushwooshLiveUpdates;

// 当用户在应用内完成活动时，关闭特定的实时更新，
// 无需等待服务器的最终“结束”推送
PushwooshLiveUpdates.endLiveUpdate("order_4521");

// 列出当前由此应用显示的活动 ID
List<String> active = PushwooshLiveUpdates.getActiveIds();

// 清除此应用正在显示的所有内容，例如在注销时
PushwooshLiveUpdates.endAllLiveUpdates();
```

所有方法都可以安全地从任何线程调用，并且在 Android 16 以下的设备上是无操作。

## 相关链接

- [Pushwoosh Android SDK 概述](/zh/developer/pushwoosh-sdk/android-sdk/)
- [Pushwoosh Android SDK API 参考](https://pushwoosh.github.io/pushwoosh-android-sdk/)
- [Messaging API](/zh/developer/api-reference/messaging-api-v2/)
- [Android 以进度为中心的通知](https://developer.android.com/about/versions/16/features/progress-centric-notifications)