IVS 聊天功能客户端消息收发 SDK:Kotlin 协同教程第 1 部分:聊天室 - Amazon IVS

IVS 聊天功能客户端消息收发 SDK:Kotlin 协同教程第 1 部分:聊天室

这是一个由两部分组成的教程的第 1 部分。通过使用 Kotlin 编程语言和协同例程构建功能齐全的 Android 应用程序,您将了解使用 Amazon IVS 聊天功能消息收发 SDK 的基本知识。我们把这个应用程序称为 Chatterbox

在开始本模块之前,请花几分钟时间熟悉先决条件、聊天令牌背后的关键概念以及创建聊天室所需的后端服务器。

这些教程是为刚接触 IVS 聊天功能消息收发 SDK 但经验丰富的 Android 开发人员创建的。您需要熟悉 Kotlin 编程语言并在 Android 平台上创建 UI。

本教程的第 1 部分分为几个章节:

要学习完整的 SDK 文档,请从亚马逊 IVS 聊天功能客户端消息收发 SDK(在《Amazon IVS 聊天功能用户指南》中)和 Chat Client Messaging: SDK for Android Reference(在 GitHub 上)开始。

先决条件

设置本地身份验证/授权服务器

您的后端服务器负责创建聊天室和生成 IVS 聊天功能 Android SDK 所需的聊天令牌,以便在您的客户端连接聊天室时进行身份验证和授权。

请参阅《Amazon IVS Chat 入门》中的创建聊天令牌。如其中的流程图所示,您的服务器端代码负责创建聊天令牌。这意味着您的应用程序必须通过向服务器端应用程序请求聊天令牌,来提供自己生成聊天令牌的方法。

我们使用 Ktor 框架创建一个实时本地服务器,该服务器使用您的本地 Amazon 环境管理聊天令牌的创建。

此时,我们希望您已正确设置 AWS 凭证。有关分步说明,请参阅 Set up AWS temporary credentials and AWS Region for development

创建一个新目录并命名为 chatterbox,在其中再创建一个,然后命名为 auth-server

我们的服务器文件夹将具有如下结构:

- auth-server - src - main - kotlin - com - chatterbox - authserver - Application.kt - resources - application.conf - logback.xml - build.gradle.kts

注意:您可以直接将此处的代码复制/粘贴到引用文件中。

接下来,我们添加所有必要的依赖项和插件,以便身份验证服务器正常工作:

Kotlin 脚本:

// ./auth-server/build.gradle.kts plugins { application kotlin("jvm") kotlin("plugin.serialization").version("1.7.10") } application { mainClass.set("io.ktor.server.netty.EngineMain") } dependencies { implementation("software.amazon.awssdk:ivschat:2.18.1") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20") implementation("io.ktor:ktor-server-core:2.1.3") implementation("io.ktor:ktor-server-netty:2.1.3") implementation("io.ktor:ktor-server-content-negotiation:2.1.3") implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.3") implementation("ch.qos.logback:logback-classic:1.4.4") }

现在我们需要为身份验证服务器设置日志记录功能。(有关更多信息,请参阅配置记录器。)

XML:

// ./auth-server/src/main/resources/logback.xml <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="trace"> <appender-ref ref="STDOUT"/> </root> <logger name="org.eclipse.jetty" level="INFO"/> <logger name="io.netty" level="INFO"/> </configuration>

Ktor 服务器需要自动从 resources 目录的 application.* 文件中加载的配置设置,所以我们也添加了这些设置。(有关更多信息,请参阅文件中的配置。)

HOCON

// ./auth-server/src/main/resources/application.conf ktor { deployment { port = 3000 } application { modules = [ com.chatterbox.authserver.ApplicationKt.main ] } }

最后,让我们实施我们的服务器:

Kotlin:

// ./auth-server/src/main/kotlin/com/chatterbox/authserver/Application.kt package com.chatterbox.authserver import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import software.amazon.awssdk.services.ivschat.IvschatClient import software.amazon.awssdk.services.ivschat.model.CreateChatTokenRequest @Serializable data class ChatTokenParams(var userId: String, var roomIdentifier: String) @Serializable data class ChatToken( val token: String, val sessionExpirationTime: String, val tokenExpirationTime: String, ) fun Application.main() { install(ContentNegotiation) { json(Json) } routing { post("/create_chat_token") { val callParameters = call.receive<ChatTokenParams>() val request = CreateChatTokenRequest.builder().roomIdentifier(callParameters.roomIdentifier) .userId(callParameters.userId).build() val token = IvschatClient.create() .createChatToken(request) call.respond( ChatToken( token.token(), token.sessionExpirationTime().toString(), token.tokenExpirationTime().toString() ) ) } } }

创建 Chatterbox 项目

要创建 Android 项目,请安装并打开 Android Studio

按照 Android 官方在创建项目指南中列出的步骤进行操作。

  • 选择您的项目中,为我们的 Chatterbox 应用程序选择空活动项目模板。

  • 配置您的项目中,为配置字段选择以下值:

    • 名称:My App

    • 程序包名称:com.chatterbox.myapp

    • 保存位置:指向在上一步中创建的 chatterbox 目录

    • 语言:Kotlin

    • 最低 API 级别:API 21:Android 5.0(Lollipop)

正确指定所有配置参数后,我们在 chatterbox 文件夹内的文件结构应如下所示:

- app - build.gradle ... - gradle - .gitignore - build.gradle - gradle.properties - gradlew - gradlew.bat - local.properties - settings.gradle - auth-server - src - main - kotlin - com - chatterbox - authserver - Application.kt - resources - application.conf - logback.xml - build.gradle.kts

现在我们有一个正在运行的 Android 项目,可以将 com.amazonaws:ivs-chat-messagingorg.jetbrains.kotlinx:kotlinx-coroutines-core 添加到 build.gradle 依赖项中。(有关 Gradle 构建工具包的更多信息,请参阅配置您的构建。)

注意:在每个代码段的顶部,都有一个文件路径,您应该在其中对项目进行更改。路径相对于项目的根目录。

Kotlin:

// ./app/build.gradle plugins { // ... } android { // ... } dependencies { implementation 'com.amazonaws:ivs-chat-messaging:1.1.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' // ... }

添加新依赖项后,在 Android Studio 中运行将项目与 Gradle 文件同步,将项目与新依赖项同步。(有关更多信息,请参阅添加构建依赖项。)

为了方便从项目根目录运行我们的身份验证服务器(在上一部分中创建),我们将其作为新模块包含在 settings.gradle 中。(有关更多信息,请参阅使用 Gradle 构建软件组件。)

Kotlin 脚本:

// ./settings.gradle // ... rootProject.name = "My App" include ':app' include ':auth-server'

从现在起,由于 auth-server 已包含在 Android 项目中,您可以从项目的根目录使用以下命令运行身份验证服务器:

Shell:

./gradlew :auth-server:run

连接到聊天室和观察连接更新

为了打开聊天室连接,我们使用了 onCreate() 活动生命周期回调,该回调在首次创建活动时触发。ChatRoom 构造函数要求我们提供 regiontokenProvider 以实例化聊天室连接。

注意:以下代码段中的 fetchChatToken 功能将在下一部分中实现。

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt package com.chatterbox.myapp // ... // AWS region of the room that was created in Getting Started with Amazon IVS Chat const val REGION = "us-west-2" class MainActivity : AppCompatActivity() { private var room: ChatRoom? = null // ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Create room instance room = ChatRoom(REGION, ::fetchChatToken) } // ... }

显示聊天室连接的变化并对其做出反应是制作 chatterbox 等聊天应用程序的重要组成部分。在我们开始与聊天室互动之前,必须订阅聊天室连接状态事件以获取更新。

在协同例程的聊天功能 SDK 中,ChatRoom 希望我们在中处理聊天室生命周期事件。目前,函数在调用时只会记录确认消息:

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt package com.chatterbox.myapp // ... const val TAG = "Chatterbox-MyApp" class MainActivity : AppCompatActivity() { // ... override fun onCreate(savedInstanceState: Bundle?) { // ... // Create room instance room = ChatRoom(REGION, ::fetchChatToken).apply { lifecycleScope.launch { stateChanges().collect { state -> Log.d(TAG, "state change to $state") } } lifecycleScope.launch { receivedMessages().collect { message -> Log.d(TAG, "messageReceived $message") } } lifecycleScope.launch { receivedEvents().collect { event -> Log.d(TAG, "eventReceived $event") } } lifecycleScope.launch { deletedMessages().collect { event -> Log.d(TAG, "messageDeleted $event") } } lifecycleScope.launch { disconnectedUsers().collect { event -> Log.d(TAG, "userDisconnected $event") } } } } }

此后,我们需要提供读取聊天室连接状态的功能。我们会将其保留在 MainActivity.kt 属性中并将其初始化为聊天室连接默认断开状态(参阅 IVS 聊天功能 Android SDK 参考中的 ChatRoom state)。为了保持最新的本地状态,我们需要实现一个状态更新器函数,我们称之为 updateConnectionState

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt package com.chatterbox.myapp // ... class MainActivity : AppCompatActivity() { private var connectionState = ChatRoom.State.DISCONNECTED // ... private fun updateConnectionState(state: ChatRoom.State) { connectionState = state when (state) { ChatRoom.State.CONNECTED -> { Log.d(TAG, "room connected") } ChatRoom.State.DISCONNECTED -> { Log.d(TAG, "room disconnected") } ChatRoom.State.CONNECTING -> { Log.d(TAG, "room connecting") } } }

接下来,我们将状态更新器函数与 ChatRoom.listener 属性集成:

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt package com.chatterbox.myapp // ... class MainActivity : AppCompatActivity() { // ... override fun onCreate(savedInstanceState: Bundle?) { // ... // Create room instance room = ChatRoom(REGION, ::fetchChatToken).apply { lifecycleScope.launch { stateChanges().collect { state -> Log.d(TAG, "state change to $state") updateConnectionState(state) } } // ... } } }

现在我们能够保存、侦听和响应 ChatRoom 状态更新,接下来可以初始化连接了:

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt package com.chatterbox.myapp // ... class MainActivity : AppCompatActivity() { // ... private fun connect() { try { room?.connect() } catch (ex: Exception) { Log.e(TAG, "Error while calling connect()", ex) } } // ... }

构建令牌提供程序

现在可以创建一个函数,负责在我们的应用程序中创建和管理聊天令牌。在此示例中,我们使用 Android 版 Retrofit HTTP 客户端

在发送任何网络流量之前,我们必须设置适用于 Android 的网络安全配置。(有关更多信息,请参阅网络安全配置。) 我们首先向应用程序清单文件添加网络权限。请注意,添加的 user-permission 标签和 networkSecurityConfig 属性将指向我们的新网络安全配置。在下面的代码中,将 <version> 替换为聊天功能 Android SDK 的当前版本号(例如 1.1.0)。

XML:

// ./app/src/main/AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.chatterbox.myapp"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:fullBackupContent="@xml/backup_rules" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" // ... // ./app/build.gradle dependencies { implementation("com.amazonaws:ivs-chat-messaging:<version>") // ... implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") }

声明您的本地 IP 地址(例如 10.0.2.2localhost)为可信域,开始与我们的后端交换消息:

XML:

// ./app/src/main/res/xml/network_security_config.xml <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">10.0.2.2</domain> <domain includeSubdomains="true">localhost</domain> </domain-config> </network-security-config>

接下来,我们需要添加一个新的依赖项以及用于解析 HTTP 响应的 Gson 转换器在下面的代码中,将 <version> 替换为聊天功能 Android SDK 的当前版本号(例如 1.1.0)。

Kotlin 脚本:

// ./app/build.gradle dependencies { implementation("com.amazonaws:ivs-chat-messaging:<version>") // ... implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") }

要检索聊天令牌,我们需要从 chatterbox 应用程序发出 POST HTTP 请求。我们在 Retrofit 接口中定义请求,以便实现。(请参阅 Retrofit 文档。还要熟悉 CreateChatToken 端点规范。)

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/network/ApiService.kt package com.chatterbox.myapp.network import com.amazonaws.ivs.chat.messaging.ChatToken import retrofit2.Call import retrofit2.http.Body import retrofit2.http.POST data class CreateTokenParams(var userId: String, var roomIdentifier: String) interface ApiService { @POST("create_chat_token") fun createChatToken(@Body params: CreateTokenParams): Call<ChatToken> } // ./app/src/main/java/com/chatterbox/myapp/network/RetrofitFactory.kt package com.chatterbox.myapp.network import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory object RetrofitFactory { private const val BASE_URL = "http://10.0.2.2:3000" fun makeRetrofitService(): ApiService { return Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build().create(ApiService::class.java) } }

现在,网络设置完成后,可以添加一个函数,负责创建和管理我们的聊天令牌。我们将其添加到 MainActivity.kt,它是在项目生成时自动创建的:

Kotlin:

// ./app/src/main/java/com/chatterbox/myapp/MainActivity.kt package com.chatterbox.myapp import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import com.amazonaws.ivs.chat.messaging.* import com.amazonaws.ivs.chat.messaging.coroutines.* import com.chatterbox.myapp.network.CreateTokenParams import com.chatterbox.myapp.network.RetrofitFactory import retrofit2.Call import java.io.IOException import retrofit2.Callback import retrofit2.Response // custom tag for logging purposes const val TAG = "Chatterbox-MyApp" // any ID to be associated with auth token const val USER_ID = "test user id" // ID of the room the app wants to access. Must be an ARN. See Amazon Resource Names(ARNs) const val ROOM_ID = "arn:aws:..." // AWS region of the room that was created in Getting Started with Amazon IVS Chat const val REGION = "us-west-2" class MainActivity : AppCompatActivity() { private val service = RetrofitFactory.makeRetrofitService() private var userId: String = USER_ID // ... private fun fetchChatToken(callback: ChatTokenCallback) { val params = CreateTokenParams(userId, ROOM_ID) service.createChatToken(params).enqueue(object : Callback<ChatToken> { override fun onResponse(call: Call<ChatToken>, response: Response<ChatToken>) { val token = response.body() if (token == null) { Log.e(TAG, "Received empty token response") callback.onFailure(IOException("Empty token response")) return } Log.d(TAG, "Received token response $token") callback.onSuccess(token) } override fun onFailure(call: Call<ChatToken>, throwable: Throwable) { Log.e(TAG, "Failed to fetch token", throwable) callback.onFailure(throwable) } }) } }

后续步骤

现在,您已经建立聊天室连接,请继续阅读本 Kotlin 协同教程的第 2 部分:消息和事件