【问题标题】:Using @Component.Builder with constructor params将 @Component.Builder 与构造函数参数一起使用
【发布时间】:2017-10-19 17:51:09
【问题描述】:

我正在尝试将 dagger 和 kotlin 和 mvvm 合二为一,所以如果这个问题很奇怪,请原谅我。

如果我有一个 NetworkModule,它基本上为应用程序提供改造,我认为传入我们想要构建改造的基本 url 是一个好主意。我可以通过应用程序的组件构建函数以 old 的方式进行操作,但不知道如何通过@Component.Builder 方法进行操作。尝试:

App.kt

DaggerAppComponent.builder()
            .application(this)
            .networkModule(BuildConfig.BASE_URL)
            .build()
            .inject(this)

AppComponent.kt

@Singleton
@Component(modules = arrayOf(
        AppModule::class,
        NetworkModule::class,
        AndroidInjectionModule::class,
        ActivityBuilder::class))
interface AppComponent {

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder

        @BindsInstance
        fun networkModule(baseUrl: String): Builder

        fun build(): AppComponent

    }

    fun inject(app: App)
}

NetworkModule.kt

@Module
class NetworkModule(baseUrl: String) {

    //Attempt to force a public setter for Dagger
    var baseUrl: String = baseUrl
        set

    @Provides
    @Singleton
    fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
        val loggingInterceptor = HttpLoggingInterceptor { message -> Timber.d(message) }
        loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        return loggingInterceptor
    }

    @Provides
    @Singleton
    fun provideOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
        val httpClientBuilder = OkHttpClient.Builder()
        if (BuildConfig.DEBUG) httpClientBuilder.addInterceptor(httpLoggingInterceptor)
        return httpClientBuilder.build()
    }

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
    }
}

错误很清楚,但我仍然不明白它的含义:) @Component.Builder is missing setters for required modules or components: [core.sdk.di.module.NetworkModule]。我试图强制模块为基本 url 创建一个公共设置器(尽管我认为这不是必需的)。

【问题讨论】:

    标签: android dependency-injection kotlin dagger-2


    【解决方案1】:

    要创建一个组件,需要提供所有模块。现在 Dagger 可以创建任何本身具有无参数构造函数的模块,因此您不必将其传入。

    @Module
    class NetworkModule(baseUrl: String) { /* ... */ }
    

    您声明了一个在其构造中具有参数的模块,因此 Dagger 无法构造它。这是错误试图告诉你的:

    @Component.Builder 缺少所需模块或组件的设置器:[core.sdk.di.module.NetworkModule]

    您有一个必须手动添加的模块(因为 Dagger 无法构建它)但您的 @Component.Builder 没有办法包含它。

    您提供的是将String 绑定为您的基本网址,这不是最好的主意,因为您可能希望在项目中使用String 的多个对象。您应该考虑使用@Qualifier,稍后会详细介绍。

    @Component.Builder
    interface Builder {
    
      @BindsInstance
      fun application(application: Application): Builder
    
      // binds a `String` to the component
      @BindsInstance
      fun networkModule(baseUrl: String): Builder
    
      // no method to set the NetworkModule!
    
      fun build(): AppComponent
    }
    

    要解决您的问题,您有 2 个选项。您可以自己创建一个 setter 并将 url 传递给模块构造函数,然后在组件 (1) 上设置模块,或者您可以绑定 url 并简单地在模块中使用它,删除参数来自构造函数(2)


    (1) 自己创建模块

    这是一种更简单的方法,因为您只需自己创建模块并将其传递给 Dagger 组件构建器。您所要做的就是用模块的 setter 替换 String 的绑定。

    @Component.Builder
    interface Builder {
    
      @BindsInstance
      fun application(application: Application): Builder
    
      // add the module manually
      fun networkModule(networkModule: NetworkModule): Builder
    
      fun build(): AppComponent
    }
    

    创建组件时,您现在必须将模块添加到构建器。

    builder
      .application(app)
      .networkModule(NetworkModule(MY_BASE_URL)) // create the module yourself
      .build()
    

    (2)实际绑定url

    要将 url 绑定到组件并在您的模块中简单地使用它(如您所愿),您必须从构造函数中删除参数。

    @Module
    class NetworkModule() { /* ... */ } // no-arg constructor
    

    通过这样做,Dagger 现在可以创建模块,您将摆脱该错误。我们不想将 url 放在构造函数中,而是希望通过 Dagger 传入它,但如上所述,使用 String 并不是最优的,因为它实际上告诉 Dagger 你想要 thatString。您应该使用限定符来添加适当的区别,例如@Named("baseUrl") String.
    More information about qualifiers

    @Component.Builder
    interface Builder {
    
      @BindsInstance
      fun application(application: Application): Builder
    
      // bind a `String` named "baseUrl" to this component
      @BindsInstance
      fun baseUrl(@Named("baseUrl") baseUrl: String): Builder
    
      fun build(): AppComponent
    }
    

    Dagger 现在知道有一个@Named("baseUrl") String,我们现在也可以在我们的模块中使用它。

    @Module
    class NetworkModule() {
    
      // other methods omitted
    
      @Provides
      @Singleton
      fun provideRetrofit(
              // request a String named baseUrl from Dagger
              @Named("baseUrl") baseUrl: String, 
              okHttpClient: OkHttpClient
      ): Retrofit {
        return Retrofit.Builder()
                .baseUrl(baseUrl)
                // ...
                .build()
      }
    

    就是这样。

    【讨论】:

    • 非常有趣,谢谢,选项 2 看起来不错!我花了一段时间才弄清楚选项 1 的关键是删除 @BindsInstance 注释。匕首是一个难以破解的坚果:D
    【解决方案2】:

    @Component.Builder 缺少所需模块或组件的设置器:[core.sdk.di.module.NetworkModule]

    这个错误非常直接,构建器缺少一个方法来指定确切的类型NetworkModule,否则它不知道如何构建。

    @BindsInstance
    fun networkModule(baseUrl: String): Builder
    

    这段代码绑定了String类型,对NetworkModule的创建没有影响。由于NetworkModule 无法注入且没有零参数构造函数,因此必须直接使用构建器的类型进行设置:

    fun networkModule(module: NetworkModule): Builder
    

    App.kt 中,然后用.networkModule(NetworkModule(BuildConfig.BASE_URL)) 重写构建器,它应该会成功构建。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 2013-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多