美文网首页
Dagger小技巧之OkHttp延迟初始化

Dagger小技巧之OkHttp延迟初始化

作者: 珞泽珈群 | 来源:发表于2020-05-12 15:55 被阅读0次

前言

偶然间看到了这个关于Dagger小技巧的系列,很实用,也不复杂,在此我搬运转述一下。本文并非翻译,只是概述,想要更详细地了解,请查看原文:
Dagger Party Tricks: Deferred OkHttp Initialization

其它技巧:
Dagger小技巧之私有依赖
Dagger小技巧之Kotlin扩展函数

目的

利用Dagger延迟OkHttp的初始化,并且把初始化过程放到后台线程,避免阻塞主线程。

问题

@Module
object ApiModule {
    @Provides
    fun provideCache(ctx: Context): Cache {
      return Cache(ctx.cacheDir, CACHE_SIZE)
    }

    @Provides
    fun provideClient(cache: Cache): OkHttpClient {
      return OkHttpClient.Builder()
          .cache(cache)
          .build()
    }

    @Provides
    fun provideRetrofit(client: OkHttpClient): Retrofit {
      return Retrofit.Builder()
          .baseUrl("https://example.com")
          .client(client)
          .build()
    }

    @Provides
    fun provideApi(retrofit: Retrofit): MyApi {
      return retrofit.create(MyApi::class.java)
    }
}

类似这样的代码,在使用Retrofit+Dagger的App中很常见,几乎是固定的套路。然后你就可以愉快地使用MyApi了,像是这样:

class MainController @Inject constructor(private val api: MyApi) {
    suspend fun onLoad() {
        val result = api.fetchStuff()
    }
}

那么问题来了,依赖注入何时被初始化的呢?毫无疑问,借助Retrofit的CallAdapter,网络请求都是在后台线程发出的(例如RxJava,Coroutines),但是依赖注入缺是在它被调用的线程初始化的,对于上例而言,当我们通过Dagger获取MainController时,MyApi才会被初始化创建(你可以把MyApi定义成@Singleton的,但MyApi总是存在一个初始化的过程),MyApi初始化也就意味着需要创建Cache,OkHttpClientRetrofit,这一切都发生在依赖注入被调用的线程,一般而言就是主线程,这并非什么不可接受的问题,但是的确存在着一定的开销,创建Cache可能会存在一些IO操作,创建OkHttpClient会使用TrustManagerFactory,也可能需要100ms左右,总之这一切都不是free的,最好还是远离主线程,放在后台线程。

解决方案

你可能会想着这么做:

class MainController @Inject constructor(private val api: Lazy<MyApi>) {
    suspend fun onLoad() {
        val result = api.get().fetchStuff()
    }
}

借助Dagger Lazy,我们就可以把对MyApi的初始化延迟到使用时了(onLoad()调用时),但是这并不是一种很好的选择,Lazy是可以把MyApi的初始化延迟到使用时,但是我们并不能保证使用时(onLoad()调用时)就不是发生在主线程,你当然可以选择在onLoad()调用时单独开个线程,但是这种依赖于使用时的“线程保证”是一种很奇怪的做法,因为你不确定MyApi会在哪里被第一次使用(或许还有个方法叫onLoad2())。

让我们把目光重新聚焦到Retrofit。

@Provides
fun provideRetrofit(client: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .client(client)
        ...
}

众所周知,Retrofit依赖于OkHttp,但是这样说并不严谨,其实Retrofit仅仅依赖于Call.Factory接口,而OkHttpClient仅仅是Call.Factory的一种实现,上面这段代码只是如下代码的简写:

@Provides
fun provideRetrofit(client: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .callFactory(client)
        ...
}

Call.Factory又是个SAM接口,所以可以使用一个lambda进行代理:

@Provides
fun provideRetrofit(client: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .callFactory { client.newCall(it) }
        ...
}

这时候我们再使用Dagger Lazy:

@Provides
fun provideRetrofit(client: Lazy<OkHttpClient>): Retrofit {
    return Retrofit.Builder()
        .callFactory { client.get().newCall(it) }
        ...
}

最美妙的地方到了,Call.Factory是会在后台线程被调用的,而这正是我们想要的,并且这一切对于外部使用者而言是透明的,实在是严丝合缝的美妙。

我们以0代价,不仅做到了OkHttp的延迟初始化,并且把初始化放到了后台线程,还有这好事,你还在等啥。

相关文章

  • Dagger小技巧之OkHttp延迟初始化

    前言 偶然间看到了这个关于Dagger小技巧的系列,很实用,也不复杂,在此我搬运转述一下。本文并非翻译,只是概述,...

  • dagger2+retrofit [Dagger/Nullab

    错误: [Dagger/Nullable] okhttp3.HttpUrl is not nullable, bu...

  • EffectiveJava第十章第六节

    慎用延迟初始化 延迟初始化作为一种性能优化的技巧,它要求类的域成员在第一次访问时才执行必要的初始化动作,而不是在类...

  • Dagger小技巧之私有依赖

    前言 偶然间看到了这个关于Dagger小技巧的系列,很实用,也不复杂,在此我搬运转述一下。本文并非翻译,只是概述,...

  • OkHttp源码之磁盘缓存的实现

    在上篇文章okhttp源码之缓存文件介绍中,我们大致介绍了okhttp磁盘缓存的形式以及缓存文件的初始化,这篇文章...

  • Dagger小技巧之Kotlin扩展函数

    前言 偶然间看到了这个关于Dagger小技巧的系列,很实用,也不复杂,在此我搬运转述一下。本文并非翻译,只是概述,...

  • Java对象延迟初始化的实现

    一、什么是延迟初始化? 在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。延迟初始化...

  • 关于延迟初始化

    最近常在代码中见到延迟初始化,想和大家聊聊这个小话题。最简单的延迟初始化很容易想,提起键盘我就能敲出来啊: 这个延...

  • 使用 okHttp

    MVP+okHttp+Retrofit+RxJava+Glide+Dagger 是现在最流行的一套技术框架, MV...

  • Fiddler 对App抓包代理问题

    App 防止 Fiddler 抓包小技巧fiddler 抓不到app包 抓不到okhttp/asynchttpcl...

网友评论

      本文标题:Dagger小技巧之OkHttp延迟初始化

      本文链接:https://www.haomeiwen.com/subject/tpgbnhtx.html