Кинжал 2 - Как установить родственную зависимость через субкомпонентную или компонентную зависимость - PullRequest
0 голосов
/ 06 ноября 2019

Фон

В соответствии с документами зависимость между подкомпонентами одноуровневого элемента невозможна, поэтому остальным способом остается зависимость компонента. Я пытаюсь инкапсулировать класс «Репозиторий», чтобы пользовательский интерфейс мог обращаться только к экземпляру «Репозитория», а не к деталям реализации «Репозитория». Я имею в виду, что ничего из ApiModule не должно быть открыто.

ApiModule

@Module
class ApiModule {
    @Provides
    fun provideConnection(): Connection = //....   
}

RepositoryModule

@Module (
    includes = [
        ApiModule::class
    ]
)
abstract class RepositoryModule {
    @Provides
    fun providesRepository(connection Connection): Repository = //.....
}

AppComponent

@Component (modules = [
    AppModule::class,
    RepositoryModule::class
])
interface AppComponent{
    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder
        fun build(): AppComponent
    }
}

Задача

Теперь, если я создаю подкомпонент (скажем, ActivitySubComponent ) из AppComponent , все привязки в модулях RepositoryModule и ApiModule будут доступны этому подкомпоненту. Однако я хочу, чтобы только "Репозиторий" был доступен из ActivitySubComponent

Предлагаемое решение

Создать RepositoryComponenet и ActivityComponent компоненты составляют AppComponenet их зависимость:

AppComponent

@Component (modules = [
    AppModule::class
])
interface AppComponent{
    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder
        fun build(): AppComponent
    }
}

ActivityComponent

@Component(dependencies = [AppComponent::class], modules = [ActivityModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)

    @Component.Builder
    interface Builder{

        fun appComponent(appComponent: AppComponent): Builder

        fun build(): ActivityComponent
    }
} 

RepositoryComponent

@Component(dependencies = [AppComponent::class], modules = [RepositoryModule::class])
interface RepositoryComponent {
}

Проблема заключается в том, как мне представить экземпляр "Repository" в одноуровневом элементе ActivityComponent привязки или @Inject в MainActivity? Будем благодарны за любые объяснения, предложения по изменению моей конфигурации или ссылки на статьи, которые могут решить мою проблему.

1 Ответ

0 голосов
/ 11 ноября 2019

Если я правильно понимаю вашу проблему, вы не хотите раскрывать все зависимости от AppComponent его дочерним компонентам. Как говорится в документации по компонентам (раздел Зависимости компонентов),

Обратите внимание, что только зависимости, предоставляемые в качестве методов предоставления, доступны через зависимости компонентов.

youДля этой цели следует использовать зависимости компонентов.

Рабочий пример ниже

class Connection
interface Repository

@Module
object AppModule {
    @Provides
    @Singleton
    fun provideConnection() = Connection()
}

@Module(includes = [AppModule::class])
object RepositoryModule {
    @Provides
    @Singleton
    fun provideRepository(connection: Connection) = object : Repository {}
}

@Singleton
@Component(modules = [AppModule::class, RepositoryModule::class])
interface AppComponent {
    val repository: Repository
}

@Scope
@MustBeDocumented
@kotlin.annotation.Retention
annotation class ActivityScope

@ActivityScope
@Component(dependencies = [AppComponent::class])
interface ActivityComponent {
    fun inject(activity: FakeActivity)
}

class FakeActivity {
    @Inject
    lateinit var repository: Repository
    //    uncomment this and code won't compile because connection isn't explicitly exposed in AppComponent
    //    @Inject
    //    lateinit var connection: Connection
}

fun main() {
    val appComponent = DaggerAppComponent.create()
    val activityComponent = DaggerActivityComponent.builder().appComponent(appComponent).build()
    val fakeActivity = FakeActivity()
    activityComponent.inject(fakeActivity)
    println("hash: ${fakeActivity.repository}")
    println("hash: ${appComponent.repository}")
}

Я использовал области действия, потому что я полагаю, что вы хотите только один экземпляр репозитория на приложение. Если что-то не понятно, дайте мне знать.

...