KMP 中的 Ktor

社区移动开发Android

picture.image

KMP 中的 Ktor

如你所见, 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. 因为Ktorcoroutines版本只支持单线程使用.

现在, 让我们使用 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, 并将 isLenientignoreUnknownKeys 设置为 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 来记录所有的日志.

picture.image

好吧, 今天的内容就分享到这里啦!

一家之言, 欢迎拍砖!

Happy Coding! Stay GOLDEN!

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论