> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.so/llms.txt
> Use this file to discover all available pages before exploring further.

# 展示第一个广告

> 创建会话、喂入消息、挂载 InlineAd 并订阅事件。

这是 5 分钟接入的完整流程。开始之前，请确认你已经完成 [安装](/sdk/kotlin/installation)。

## 1. 创建会话

入口是 `KontextAds.createSession(context, options)`，它返回一个 `Session`——后续整个对话生命周期都通过它来操作。

```kotlin theme={null}
import java.net.URI
import so.kontext.ads.KontextAds
import so.kontext.ads.model.Character
import so.kontext.ads.model.SessionOptions

val session = KontextAds.createSession(
    context = applicationContext,
    options = SessionOptions(
        publisherToken = "<your-publisher-token>",
        userId = "user-1234",
        conversationId = "conv-5678",
        character = Character(
            id = "character-1234",
            name = "John Doe",
            avatarUrl = URI.create("https://example.com/avatar.png"),
            greeting = "Hello, how can I help you today?",
        ),
        onEvent = { event ->
            // 处理广告生命周期事件
            android.util.Log.d("kontext", "${event.name} $event")
        },
    ),
)
```

会话在其生命周期内会固定 `publisherToken`、`userId` 和 `conversationId`。当这些值变化时（例如用户开始新对话），请重建会话。

<Note>
  请传入 `context.applicationContext` 而不是 Activity 上下文——Session 的生命周期通常长于任意单个 Activity，SDK 会内部管理 WebView 资源。
</Note>

## 2. 喂入对话消息

每条消息出现时都要 add 到会话中。用户消息会在后台触发一次防抖后的预加载；助手消息让 SDK 把匹配到的广告绑定到对应的广告位上。

```kotlin theme={null}
import so.kontext.ads.model.Message
import so.kontext.ads.model.Role

session.addMessage(
    Message(id = "msg-1", role = Role.USER, content = "Hello, how are you?"),
)

session.addMessage(
    Message(id = "msg-2", role = Role.ASSISTANT, content = "I am good, thank you!"),
)
```

`addMessage(...)` 是同步返回的——它不是 `suspend` 函数。预加载的结果会稍后通过 `onEvent` 回调送达（`Filled`、`NoFill`、`Error` ……）——不会通过返回值返回。

## 3. 渲染广告

推荐使用 `InlineAd` composable。传入助手消息的 `messageId` 与 `Session` 即可，剩下的它都会处理：

```kotlin theme={null}
import androidx.compose.runtime.Composable
import so.kontext.ads.ui.InlineAd

@Composable
fun ChatRow(message: ChatMessage, session: Session) {
    MessageBubble(message)

    if (message.role == Role.ASSISTANT) {
        InlineAd(messageId = message.id, session = session)
    }
}
```

`InlineAd` 在重组之间是幂等的——它内部调用 `session.createAd(messageId)`，对同一个 `(messageId, code, theme)` 已存在的 `Ad` 直接复用。把广告滚出可视区再滚回来**不会**重建 iframe。

如果你的应用没有使用 Compose，请改用 `InlineAdView`——见 [实践指南 → View 互操作](/sdk/kotlin/guides#view-互操作非-compose)。

## 4. 释放资源

对话结束或页面关闭时调用 `session.destroy()`。该方法是幂等的，且是取消待处理网络请求、释放 WebView 资源的必要操作。

在 Compose 中：

```kotlin theme={null}
DisposableEffect(session) {
    onDispose { session.close() } // 等价于 session.destroy()
}
```

## 订阅事件

`AdEvent` 既可以通过 `SessionOptions` 里的 `onEvent` 回调拿到，也可以通过 `Session.events` Flow 拿到——任选其一。两者会在主线程上送达相同的事件。

### `onEvent` 回调

```kotlin theme={null}
val session = KontextAds.createSession(
    context = applicationContext,
    options = SessionOptions(
        publisherToken = "...",
        userId = "...",
        conversationId = "...",
        onEvent = { event ->
            when (event) {
                is AdEvent.Filled -> println("ad filled: ${event.bidId} revenue=${event.revenue}")
                is AdEvent.Clicked -> println("clicked: ${event.url}")
                else -> Unit
            }
        },
    ),
)
```

### Flow

```kotlin theme={null}
import kotlinx.coroutines.flow.collect

lifecycleScope.launch {
    session.events.collect { event ->
        println("event: ${event.name}")
    }
}
```

完整事件列表见 [API 参考](/sdk/kotlin/api)。
