IVS 聊天功能客户端消息收发 SDK:Kotlin 协同教程第 1 部分:聊天室
这是一个由两部分组成的教程的第 1 部分。通过使用 Kotlin
在开始本模块之前,请花几分钟时间熟悉先决条件、聊天令牌背后的关键概念以及创建聊天室所需的后端服务器。
这些教程是为刚接触 IVS 聊天功能消息收发 SDK 但经验丰富的 Android 开发人员创建的。您需要熟悉 Kotlin 编程语言并在 Android 平台上创建 UI。
本教程的第 1 部分分为几个章节:
要学习完整的 SDK 文档,请从亚马逊 IVS 聊天功能客户端消息收发 SDK(在《Amazon IVS 聊天功能用户指南》中)和 Chat Client Messaging: SDK for Android Reference
先决条件
-
熟悉 Kotlin 并在 Android 平台上创建应用程序。如果您对创建 Android 应用程序不熟悉,请学习面向 Android 开发者的构建您的第一个应用程序
指南中的基础知识。 -
阅读并理解 Amazon IVS Chat 入门。
-
使用现有的 IAM policy 中定义的
CreateChatToken
和CreateRoom
功能创建 AWS IAM 用户。(请参见 Amazon IVS Chat 入门。) -
确保该用户的私有密钥/访问密钥存储在 Amazon 凭证文件中。有关说明,请参阅 Amazon CLI 用户指南(尤其是配置和凭证文件设置部分)。
-
创建聊天室并保存其 ARN。请参阅 Amazon IVS Chat 入门。(如果不保存 ARN,您可以稍后使用控制台或 Chat API 查找 ARN。)
设置本地身份验证/授权服务器
您的后端服务器负责创建聊天室和生成 IVS 聊天功能 Android SDK 所需的聊天令牌,以便在您的客户端连接聊天室时进行身份验证和授权。
请参阅《Amazon IVS Chat 入门》中的创建聊天令牌。如其中的流程图所示,您的服务器端代码负责创建聊天令牌。这意味着您的应用程序必须通过向服务器端应用程序请求聊天令牌,来提供自己生成聊天令牌的方法。
我们使用 Ktor
此时,我们希望您已正确设置 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>
Ktorresources
目录的 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-messagingbuild.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() 活动生命周期回调region
和 tokenProvider
以实例化聊天室连接。
注意:以下代码段中的 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
属性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.2
和 localhost
)为可信域,开始与我们的后端交换消息:
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 文档
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 部分:消息和事件。