在 Android 中使用 Ktor 请求

Ktor 除了可以完成服务端的开发,同样也可以用于客户端,在 Android 可以简单的用 Ktor 来取代掉以往的各种请求响应,天生协程的 Ktor 可以方便的完成很多事情。

那么下面来写一下代码吧,首先引用相应的库:

implementation "io.ktor:ktor-client-android:1.1.5"

然了个后,使用 Android 端的 HttpClient 就可以了:

var ret: String? = null
val client = HttpClient(Android) {
    engine {
        connectTimeout = 100_000
        socketTimeout = 100_000
    }
}
try {
    ret = client.get<String>(url) { }
} catch (e: Exception) {
} finally {
    client.close()
}
return ret

这段代码用在 Ktor 后端程序时没有任何的问题,然而对于 Android 来说,没有天生协程是一个很大的问题,因为 client.get() 是一个协程方法,也就是调用者必须是被 suspend 关键字标记的。

如下所示:

suspend fun req(url: String): String { ... }

这个方法在 Android 的上下文内是不可能被直接调用的,没有任何上下文可以接受 suspend 方法。这个时候我们需要自己创建一个协程。

按通常的线程写法,我们也可以提炼出协程的写法,线程的 kotlin 写法如下:

thread {
    runOnMainThread {
        ... ...
    }
}

所以可以把协程也弄成类似的形式。


那么下面来解决协程的问题,首先还是得引用协程的库:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"

这里需要注意,千万不要用 1.1.0 以下版本的协程库,那个版本的主线程调度有 bug,会引起一个 crash。

然后写两个方法:

fun coroutine(block: suspend () -> Unit) { CoroutineScope(Dispatchers.IO).launch { block() } }
fun coroutineMain(block: suspend () -> Unit) { CoroutineScope(Dispatchers.Main).launch { block() } }

很明显的,这就是协程的壳子了,把上面的代码写进去就好:

fun req(url: String, callback:(String?) -> Unit) = coroutine {
    var ret: String? = null
    val client = HttpClient(Android) {
        engine {
            connectTimeout = 100_000
            socketTimeout = 100_000
        }
    }
    try {
        ret = client.get<String>(url) { }
    } catch (e: Exception) {
    } finally {
        client.close()
    }
    coroutineMain { callback(ret) }
}

到此,就可以在 Android 里愉快的使用 Ktor 了。

最后,如果因为包含了 ktor-clientkotlinx-coroutines 带来了编译问题,可以在 Gradle 内加入以下代码来解决:

packagingOptions {
    exclude 'META-INF/kotlinx-io.kotlin_module'
    exclude 'META-INF/atomicfu.kotlin_module'
    exclude 'META-INF/kotlinx-coroutines-io.kotlin_module'
    exclude 'META-INF/kotlinx-coroutines-core.kotlin_module'
}

推荐阅读更多精彩内容