通过模块与注入构造函数配置 Dagger 2

Dagger 2 provisioning via Modules vs Inject constructor

我正在尝试将 dagger-android 添加到 Kotlin 项目中,但在需要创建模块时以及仅声明 Inject 构造器就足够时感到困惑。

假设有以下依赖关系图:

Activity
    -> ViewModel
        -> Repository
            -> Webservice
            -> Dao
                -> Database
                    -> Application

Activity 提供 ViewModel 我们为 activity 和 ViewModel 工厂创建各自的模块,然后在 Activity 中手动创建 ViewModel,如下所示:

@Module
abstract class ActivityModule {
    @ContributesAndroidInjector
    abstract fun mainActivity(): MainActivity
}

// Skiping ViewModelKey and ViewModelFactory code for brevity
@Module
abstract class ViewModelModule {
    @Binds
    internal abstract fun bindViewModelFactory(
        factory: ViewModelFactory
    ): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    internal abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
}

class MainActivity : DaggerAppCompatActivity() {
    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        viewModel = ViewModelProviders.of(this, viewModelFactory)
            .get(HomeViewModel::class.java)
    }
}

要为 ViewModel 提供 Repository,我们只需像这样声明 @Inject constructor

class MainViewModel @Inject constructor(private val repository: Repository): ViewModel() {}

为了为 Repository 提供 WebserviceDao 以及为 Dao 提供 Database 我们创建各自的模块,如下所示:

@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideWebservice() = Webservice.create()
}

interface Webservice {
    ...
    companion object Factory {
        fun create(): Webservice {
            ...
            return retrofit
        }
    }
}


@Module
class DataModule {
    @Provides
    @Singleton
    fun provideApplicationDatabase(app: Application) =
        AppDatabase.getDatabase(app)

    @Provides
    @Singleton
    fun provideUserDao(db: AppDatabase) = db.userDao()
}

@Database(...)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        fun getDatabase(context: Context): AppDatabase {
            ...
            return instance
        }
    }
}

并且 Application 是通过 AppComponent 和应用程序中的一些魔法为 Dabatabase 提供的 class

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    NetworkModule::class,
    DataModule::class,
    ViewModelModule::class,
    ActivityModule::class
])
interface AppComponent: AndroidInjector<App> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun create(application: Application): Builder
        fun build(): AppComponent
    }
}

class App : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
        DaggerAppComponent.builder().create(this).build()
}

问题是:

  1. Database如何获取Application实例?神奇的是 AndroidSupportInjectionModule 吗?
  2. 为什么我们需要为 Web 服务和数据库而不是存储库创建模块? 是否可以注释 Web 服务接口和数据库 class 本身以跳过为它们创建单独的匕首模块?

问题1Database如何获取Application实例?是 AndroidSupportInjectionModule 神奇吗?

回答: 不,这不是 AndroidSupportInjectionModule 的工作。 AndroidSupportInjectionModule 包含在 Dagger Android 支持中,这有助于

"Configures bindings to ensure the usability of dagger.android and dagger.android.support framework classes." Found Here

所以,基本上你只是在创建 Dagger Builder 时传递应用程序上下文,你现在只需从应用程序传递它 Class 一旦你在主组件中获得它,你就在所有模块中拥有应用程序上下文我们在初始化房间数据库时需要上下文。

问题2为什么我们需要为Webservice和数据库而不是存储库创建模块?是否可以注释 Web 服务接口和数据库 class 本身以跳过为它们创建单独的匕首模块?

答案: 首先,始终尝试实现构造函数注入。 Modules的大意是"If we don't own the class we can't @annotate its constructor so we make Modules to provide their implementation"。同样,如果我们想注入接口,我们可以通过其构造函数注入通过其实现 class 来实现它。
所以我们不拥有 WebService 和数据库的初始化,这就是为什么我们创建它们的模块并提供它们以便我们可以在我们的存储库中获取它们的实例。我们拥有我们的存储库 class,因此我们可以通过构造函数注入来注入它们。