如何在 Multiplatform Ktor 和 Coil 之间共享 HttpClient?
Posted
技术标签:
【中文标题】如何在 Multiplatform Ktor 和 Coil 之间共享 HttpClient?【英文标题】:How to share HttpClient between Multiplatform Ktor and Coil? 【发布时间】:2021-10-06 02:07:31 【问题描述】:我想使用 Coil 图像库从 api 加载图像,使用之前设置的相同 cookie。因此,我想为我的 Ktor 网络调用和使用线圈的图像加载使用相同的 HttpClient。
如何在 Ktor 和 Coil 之间共享同一个 HttpClient?我想,我需要以某种方式调整依赖关系,但我无法理解它。
共享模块中的我的 KtorApiImpl
class KtorApiImpl(log: Kermit) : KtorApi
val baseUrl = BuildKonfig.baseUrl
// If this is a constructor property, then it gets captured
// inside HttpClient config and freezes this whole class.
@Suppress("CanBePrimaryConstructorProperty")
private val log = log
override val client = HttpClientProvider().getHttpClient().config
install(JsonFeature)
serializer = KotlinxSerializer()
install(Logging)
logger = object : Logger
override fun log(message: String)
log.v("Network") message
level = LogLevel.INFO
init
ensureNeverFrozen()
override fun HttpRequestBuilder.apiUrl(path: String)
url
takeFrom(baseUrl)
encodedPath = path
override fun HttpRequestBuilder.json()
contentType(ContentType.Application.Json)
androidMain 中的实际 HttpClientProvider
var cookieJar: CookieJar = object : CookieJar
private val cookieStore: HashMap<String, List<Cookie>> = HashMap()
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>)
cookieStore[url.host] = cookies
override fun loadForRequest(url: HttpUrl): List<Cookie>
val cookies = cookieStore[url.host]
return cookies ?: ArrayList()
actual class HttpClientProvider actual constructor()
actual fun getHttpClient(): HttpClient
return HttpClient(OkHttp)
engine
preconfigured = getOkHttpClient()
private fun getOkHttpClient(): OkHttpClient
return OkHttpClient.Builder()
.cookieJar(cookieJar)
.build()
androidApp 中的 ImageLoaderFactory - 如何使用 HttpClient 而不是创建新的?
class CoilImageLoaderFactory(private val context: Context) : ImageLoaderFactory
override fun newImageLoader(): ImageLoader
return ImageLoader.Builder(context)
.availableMemoryPercentage(0.25) // Use 25% of the application's available memory.
.crossfade(true) // Show a short crossfade when loading images from network or disk.
.componentRegistry
add(ByteArrayFetcher())
.okHttpClient
// Create a disk cache with "unlimited" size. Don't do this in production.
// To create the an optimized Coil disk cache, use CoilUtils.createDefaultCache(context).
val cacheDirectory = File(context.filesDir, "image_cache").apply mkdirs()
val cache = Cache(cacheDirectory, Long.MAX_VALUE)
// Lazily create the OkHttpClient that is used for network operations.
OkHttpClient.Builder()
.cache(cache)
.build()
.build()
androidApp 中的 Koin 依赖项
@Suppress("unused")
class MainApp : Application()
override fun onCreate()
super.onCreate()
initKoin(
module
single<Context> this@MainApp
single<AppInfo> AndroidAppInfo
single CoilImageLoaderFactory(get<Context>())
single<SharedPreferences>
get<Context>().getSharedPreferences("MAIN_SETTINGS", Context.MODE_PRIVATE)
single
Log.i("Startup", "Hello from Android/Kotlin!")
)
然后是主活动
class MainActivity : AppCompatActivity()
val loaderFactory: CoilImageLoaderFactory by inject()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContent
CompositionLocalProvider(LocalImageLoader provides loaderFactory.newImageLoader())
MainTheme
ProvideWindowInsets
Surface
MainScreen()
【问题讨论】:
【参考方案1】:您可以使用ImageLoader.Builder.callFactory
提供您自己的Call.Factory
用于网络请求。缺点是您必须将 KtorApiImpl
返回的任何类型映射到 Coil 可以理解的 okttp3.Response
。
这是一个示例,描述了如何实现Call.Factory
接口并将其提供给 Coil 的ImageLoader
ImageLoader.Builder(context)
.callFactory
Call.Factory
object: Call
private var job: Job? = null
override fun clone(): Call
TODO(“Not yet implemented”)
override fun request(): Request
return it
override fun execute(): Response
return runBlocking
// Call KTOR client here
override fun enqueue(responseCallback: Callback)
// Use a proper coroutines scope
job = GlobalScope.launch
// Call KTOR client here
override fun cancel()
job?.cancel()
override fun isExecuted(): Boolean
return job?.isCompleted ?: false
override fun isCanceled(): Boolean
return job?.isCancelled ?: false
override fun timeout(): Timeout
// Your Timeout here
【讨论】:
【参考方案2】:我通过 ImageLoader 访问了 OkHttpClient
class CoilImageLoaderFactory(private val context: Context) : ImageLoaderFactory, KoinComponent
val ktorApiImpl: KtorApi by inject()
override fun newImageLoader(): ImageLoader
return ImageLoader.Builder(context)
.componentRegistry
add(ByteArrayFetcher())
.okHttpClient
val config = ktorApiImpl.client.engine.config as OkHttpConfig
config.preconfigured as OkHttpClient
.build()
【讨论】:
以上是关于如何在 Multiplatform Ktor 和 Coil 之间共享 HttpClient?的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin Multiplatform - 使用 Ktor 处理响应 http 代码和异常
kotlin.native.concurrent.InvalidMutabilityException:在 Kotlin Multiplatform (iOS) 中使用 ktor 时冻结 <ob
在 Kotlin MultiPlatform 项目中未解决 iOS 依赖项
Kotlin Multiplatform Cocoapods 集成问题