将@ Component.Builder与构造函数参数

时间:2017-10-19 17:51:09

标签: android dependency-injection kotlin dagger-2

我试图学习匕首和kotlin以及mvvm,如果这个问题很奇怪,请原谅我。

如果我有一个NetworkModule,它基本上为应用程序提供了改进,我认为传递我们想要构建改造的基本URL是个好主意。我可以通过App的组件构建功能传递方式,但不能通过@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]。我试图强制模块为基本网址创建公共设置器(虽然我认为不需要)。

2 个答案:

答案 0 :(得分:19)

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

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

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

  

@ Component.Builder缺少必需模块或组件的setter:[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组件构建器。您所要做的就是将String的绑定替换为模块的setter。

@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绑定到组件并在模块中使用它 - 就像你想要的那样 - 你必须从构造函数中删除参数。

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

通过这样做,Dagger现在可以创建模块,您将摆脱该错误。我没有把url放在构造函数中,而是让它由Dagger传递,但如上所述使用String并不是最佳的,因为它实际上告诉Dagger你想要那个值对于任何String。您应该使用限定符来添加适当的区别,例如@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()
  }

就是这样。

答案 1 :(得分:3)

  

@ Component.Builder缺少必需模块或组件的setter:[core.sdk.di.module.NetworkModule]

错误是非常字面的,构建器缺少一个方法来指定它不知道如何构建的确切类型R

NetworkModule

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

NetworkModule

fun networkModule(module: NetworkModule): Builder 中,然后使用App.kt重写构建器,它应该成功构建。