如你所见, Ktor 是由 Jetbrains 创建和维护的开源库, 可用于客户端和服务器应用. Ktor 是用 Kotlin 编写的跨平台网络库, 使用 coroutines 进行异步调用.
在项目中设置 Ktor:
转到
shared
>build.gradle.kts
>commonMain
, 添加 Ktor 依赖项.
// ktor
object Versions{
const val ktor = "2.0.0-beta-1"
}
object Ktor{
const val client_core = "io.ktor:ktor-client-core:${Versions.ktor}"
const val client_serialization = "io.ktor:ktor-client-serialization:${Versions.ktor}"
}
val commonMain by getting {
dependencies {
//put your multiplatform dependencies here
// ktor
with(Ktor){
implementation(client_core)
implementation(client_serialization)
}
}
}
这里我们添加了 Ktor 核心库 + 序列化库.
转到
shared
>build.gradle.kts
>androidMain
为 Android 平台添加 Ktor HTTP 客户端引擎.
// ktor
object Versions{
const val ktor = "2.0.0-beta-1"
}
// Ktor
object Ktor{
// Android
const val client_android = "io.ktor:ktor-client-android:${Versions.ktor}"
}
val androidMain by getting {
dependencies {
// Ktor Android
implementation(Ktor.client_android)
}
}
转到
shared
>build.gradle.kts
>iosMain
为 iOS 平台添加 Ktor HTTP 客户端引擎.
// ktor
object Versions{
const val ktor = "2.0.0-beta-1"
// Coroutine native
const val coroutine_native_core = "1.6.0-native-mt"
}
// Coroutines
object Coroutines {
const val coroutine_native = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutine_native_core}"
}
// Ktor
object Ktor{
// iOS
const val client_ios = "io.ktor:ktor-client-ios:${Versions.ktor}"
}
val iosMain by getting {
dependsOn(commonMain)
dependencies {
// Ktor iOS
implementation(Ktor.client_ios)
// Coroutines Native
implementation(Coroutines.coroutine_native) {
version {
strictly(Versions.coroutine_native_core)
}
}
}
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
注意: 我们为 iOS 使用了特定版本的kotlinx-coroutines. 因为Ktor的coroutines版本只支持单线程使用.
现在, 让我们使用 Ktor 进行一次网络请求.
转到
data
>shared/src/commonMain
>NewzFeedAPI.kt
.
public const val NEWZ_FEED_URL = "https://newzfeedapi.org/v2/top-headlines"
public const val NEWZ_FEED_RESPONSE_FORMAT = ".json"
public const val NEWZ_FEED_APPEND_API_KEY = "&apiKey=$API_KEY"
@ThreadLocal
public object NewzFeedAPI {
//1
private val client: HttpClient = HttpClient()
//2
public suspend fun fetchTopHeadlines(newzFeedUrl: String = NEWZ_FEED_URL): HttpResponse = client.get(newzFeedUrl + NEWZ_FEED_APPEND_API_KEY)
}
了解代码:
@ThreadLocal
仅用于iOS(Kotlin/Native)
. 当你访问NewzFeedAPI
时, 它会创建一个新的副本, 并且不会共享线程.
//1, 我们创建一个新的
HttpClient
来发送请求
//2, 我们创建一个
suspend
函数, 该函数会发出请求, 并通过HttpResponse
作为响应返回.
注意: Ktor 支持所有 HTTP 方法, 如 GET, POST, PUT, DELETE, HEAD, OPTION, PATCH 等.
Ktor 插件
插件 1: ContentNegotiation
转到
shared
>commonMain
> 添加依赖项
object Versions {
// ktor
const val ktor = "2.0.0-beta-1"
}
object Ktor{
const val client_content_negotiation = "io.ktor:ktor-client-content-negotiation:${Versions.ktor}"
const val serialization_kotlinx_json = "io.ktor:ktor-serialization-kotlinx-json:${Versions.ktor}"
}
val commonMain by getting {
dependencies {
//put your multiplatform dependencies here
// ktor
with(Deps.Ktor){
implementation(serialization_kotlinx_json)
implementation(client_content_negotiation)
}
}
}
现在更新你的
HttpClient
对象:
const val NEWZ_FEED_URL = "https://newsapi.org/v2/top-headlines"
const val NEWZ_FEED_RESPONSE_FORMAT = ".json"
const val NEWZ_FEED_APPEND_API_KEY = "&apiKey=$API_KEY"
@ThreadLocal
public object NewzFeedAPI {
//1
private val nonStrictJson = Json { isLenient = true; ignoreUnknownKeys = true }
//2
private val client: HttpClient = HttpClient{
install(ContentNegotiation){
json(nonStrictJson)
}
}
//3
suspend fun fetchTopHeadlines(newzFeedUrl: String = NEWZ_FEED_URL): NewzContent = client.get("$newzFeedUrl$NEWZ_FEED_APPEND_API_KEY$NEWZ_FEED_RESPONSE_FORMAT").body()
}
代码分解:
//1, 我们定义了一个
nonStrictJson
属性, 其中我们定义了一个Json
, 并将isLenient
和ignoreUnknownKeys
设置为 true. 这将有助于让你的应用在未来的服务器更新中保持稳定. 这将避免因畸形 json 输入和属性而导致的错误.
//2, 然后, 我们为
json
安装ContentNegotiation
, 因此该函数的响应将是反序列化对象.ContentNegotiation
将帮助我们反序列化响应.
//3, 现在我们调用
fetchTopHeadlines
服务, 而不是HttpResponse
, 我们已经收到了反序列化对象, 我们可以直接使用它.
插件 2: 日志
转到
shared
>commonMain
> 添加依赖项.
object Versions {
// ktor
const val ktor = "2.0.0-beta-1"
}
object Ktor{
const val client_logging = "io.ktor:ktor-client-logging:${Versions.ktor}"
}
val commonMain by getting {
dependencies {
//put your multiplatform dependencies here
// ktor
with(Deps.Ktor){
implementation(client_logging)
}
}
}
转到
shared
>commonMain
>data
> 创建一个名为HttpClientLogger
的新类.
private const val TAG = "HttpClientLogger"
object HttpClientLogger : io.ktor.client.plugins.logging.Logger {
override fun log(message: String) {
Logger.d(TAG, message)
}
}
现在更新你的
HttpClient
对象:
const val NEWZ_FEED_URL = "https://newsapi.org/v2/top-headlines"
const val NEWZ_FEED_RESPONSE_FORMAT = ".json"
const val NEWZ_FEED_APPEND_API_KEY = "&apiKey=$API_KEY"
@ThreadLocal
public object NewzFeedAPI {
private val nonStrictJson = Json { isLenient = true; ignoreUnknownKeys = true }
private val client: HttpClient = HttpClient{
install(ContentNegotiation){
json(nonStrictJson)
}
//1
install(Logging) {
logger = HttpClientLogger //2
level = LogLevel.ALL
}
}
suspend fun fetchTopHeadlines(newzFeedUrl: String = NEWZ_FEED_URL): NewzContent = client.get("$newzFeedUrl$NEWZ_FEED_APPEND_API_KEY$NEWZ_FEED_RESPONSE_FORMAT").body()
}
代码分解:
//1, 我们在应用中安装
Logging
功能, 它将拦截所有的网络请求和响应.
//2, 我们使用
HttpClientLogger
类来记录所有网络通信.
//3, 我们使用
LogLevel.ALL
来记录所有的日志.
好吧, 今天的内容就分享到这里啦!
一家之言, 欢迎拍砖!
Happy Coding! Stay GOLDEN!