【问题标题】:Dynamic urls with Koin ans Retrofit带有 Koin ans Retrofit 的动态 url
【发布时间】:2019-10-12 20:37:23
【问题描述】:

在 Android 应用中使用 Retrofit 进行网络调用和 Koin 进行依赖注入,如何支持动态 url 更改?

(在使用应用程序时,用户可以切换到另一个服务器)

编辑:网络模块声明如下:

fun networkModule(baseUrl: String) = module {

    single<Api> {

        Retrofit.Builder()
                .baseUrl(baseUrl) 
                .client(OkHttpClient.Builder().readTimeout(30, TimeUnit.SECONDS)
                        .connectTimeout(30, TimeUnit.SECONDS)
                        .writeTimeout(30, TimeUnit.SECONDS)
                        .build())
                .build().create(Api::class.java)
    }

我在应用程序类 onCreate 中启动 Koin,如下所示:

 startKoin {

        if (BuildConfig.DEBUG) AndroidLogger() else EmptyLogger()

        androidContext(this@App)

        modules(listOf(networkModule(TEST_API_BASE_URL), storageModule, integrationsModule, appModule))
    }

【问题讨论】:

  • 如果没有 minimal reproducible example 显示您的 Koin 设置以及您在哪里插入 Retrofit,任何人都很难帮助您。您需要为新的基本 URL 创建一个新的 Retrofit 实例。如何将 Retrofit 实例添加到需要它的代码中取决于需要它的内容以及这些东西本身是如何创建的。您可以使用a scope 通过在 URL 更改时关闭范围来强制创建新的 Retrofit 实例。
  • 你说得对...我已经包含了代码的相关部分..
  • 如果不是作用域,那么 unloadingreloading 该模块可能会做你想做的事。
  • 您可以使用 Url 注释 public interface APIService { @GET Call getUsers(@Url String url); }

标签: android dependency-injection retrofit koin


【解决方案1】:

我最近遇到了同样的问题。最方便的方法是使用Interceptor 动态更改baseUrl。

class HostSelectionInterceptor(defaultHost: String? = null, defaultPort: Int? = null) : Interceptor {
    @Volatile var host: String? = null
    @Volatile var port: Int? = null

    init {
        host = defaultHost
        port = defaultPort
    }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
        var request = chain.request()

        this.host?.let {host->
            val urlBuilder = request.url().newBuilder()

            urlBuilder.host(host)

            this.port?.let {
                urlBuilder.port(it)
            }
            request = request.newBuilder().url(urlBuilder.build()).build()
        }

        return chain.proceed(request)
    }
}

用你的默认网址初始化它。

single { HostSelectionInterceptor(HttpUrl.parse(AppModuleProperties.baseUrl)?.host()) }
single { createOkHttpClient(interceptors = listOf(get<HostSelectionInterceptor>()))}

并在创建 OkHttpClient 时添加此拦截器。

val builder = OkHttpClient().newBuilder()    
interceptors?.forEach { builder.addInterceptor(it) }

要更改 url,您只需更新拦截器成员。

fun baseUrlChanged(baseUrl: String) {
    val hostSelectionInterceptor = get<HostSelectionInterceptor>()
    hostSelectionInterceptor.host = baseUrl
}

【讨论】:

  • 您好,先生,感谢您的回复。我对使用 koin 非常陌生。你在哪里声明和使用“baseUrlChanged(baseUrl: String)”?
  • 嘿!就我而言,我使用它在不同的测试环境之间进行更改。为此,我的应用程序在导航抽屉中有一个选择,我可以在其中选择所需的后端。在选择时,我调用此函数在我的拦截器中设置主机。并且每个新请求都将被更改为使用正确的主机。
  • @woodii 你实际上在哪里声明了baseUrlChanged?看起来像一些作用域函数?
  • 放置此功能的最佳位置是您可以更改您的 url。你的 Navigationdrawer,DebugActivity 什么的。只要确保您可以访问注入的拦截器。
【解决方案2】:

我尝试过使用 Koin 加载/卸载模块..并在很短的时间内工作,但后来,经过最小的更改后,我无法再次重新加载。

最后,我用包装对象解决了它:

class DynamicRetrofit(private val gson: Gson) {

private fun buildClient() = OkHttpClient.Builder()
        .build()

private var baseUrl = "https://etc..." //default url

private fun buildApi() = Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .client(buildClient())
        .build().create(MyApi::class.java)

var api: MyApi = buildApi()
    private set

fun setUrl(url: String) {
    if (baseUrl != url)
        baseUrl = url

    api = buildApi()
}}

我在 Koin 模块中这样声明它:

  single<DynamicRetrofit>()
    {
        DynamicRetrofit(get(), get())
    }

并以非常标准的方式使用它:

dynamicRetrofit.api.makeSomeRequest()

这对我来说是一个很好的解决方案,因为我很少更改 baseUrl。如果您需要对两个不同的服务器进行频繁的并行调用,这可能会效率低下,因为您会经常重新创建 HTTP 客户端。

【讨论】:

    猜你喜欢
    • 2018-09-22
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多