使用@ Component.Builder和构造函数params

Dan*_*son 10 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)
Run Code Online (Sandbox Code Playgroud)

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)
}
Run Code Online (Sandbox Code Playgroud)

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

错误很明显,但我仍然不明白这是什么意思:) @Component.Builder is missing setters for required modules or components: [core.sdk.di.module.NetworkModule].我试图强制模块为基本URL创建一个公共setter(虽然我认为不需要).

Dav*_*jak 27

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

@Module
class NetworkModule(baseUrl: String) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

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

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

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

您提供的是String作为基本URL 的绑定,这不是最好的主意,因为您可能希望String在项目中使用多个对象.您应该考虑使用a @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
}
Run Code Online (Sandbox Code Playgroud)

要解决您的问题,您有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
}
Run Code Online (Sandbox Code Playgroud)

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

builder
  .application(app)
  .networkModule(NetworkModule(MY_BASE_URL)) // create the module yourself
  .build()
Run Code Online (Sandbox Code Playgroud)

(2)实际绑定网址

要将url绑定到组件并在模块中使用它 - 就像你想要的那样 - 你必须从构造函数中删除参数.

@Module
class NetworkModule() { /* ... */ } // no-arg constructor
Run Code Online (Sandbox Code Playgroud)

通过这样做,Dagger现在可以创建模块,您将摆脱该错误.把网址在构造函数,而不是我们想要得到它通过在被匕首,但使用上面提到String是不是最佳的,因为它的字面告诉匕首,有你想要的是任何价值String.您应该使用限定符来添加适当的区别,例如@Named("baseUrl") String.
有关限定符的更多信息

@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
}
Run Code Online (Sandbox Code Playgroud)

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()
  }
Run Code Online (Sandbox Code Playgroud)

就是这样.

  • 非常有趣,谢谢,方案2看起来很可行!我花了一段时间才弄清楚选项1的关键是删除了@BindsInstance注释。匕首是难以破解的坚果:D (2认同)