There is an Android application Jetpack Compose. Dagger2 is used for DI (v2.45).
1. The following scopes are available:
@Scope
annotation class AppScope
@Scope
annotation class MainScreenScope
@Scope
annotation class SettingsScreenScope
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class P7LibScope
2. The Persistance package has DI modules with classes for Provides and UseCases for database manipulation:
@Module
class MappersModule {
@[Provides MainScreenScope]
fun providesBaseSettingsMapper(): Mapper<BaseSettingsDTO, BaseSettingsDB> = BaseSettingsMapper()
/* other Mappers */
}
@Module
class PersistenceModule {
@[Provides MainScreenScope]
fun providesStoreStrategy(): StoreStrategy = DatabaseStoreStrategy()
@[Provides MainScreenScope]
fun provideBaseSettingsPersistence(
baseSettingsDao: BaseSettingsDao,
mapper: Mapper<BaseSettingsDTO, BaseSettingsDB>,
storeStrategy: StoreStrategy,
): BaseSettingsPersistence = BaseSettingsPersistenceImpl(baseSettingsDao, mapper, storeStrategy)
/* other Persistance */
}
interface DatabaseUseCase {
suspend fun getShiftParams(): ShiftParamsDTO
suspend fun getBaseSettings(): BaseSettingsDTO
suspend fun getReceiptNumber(): Long
suspend fun addTransaction(transactionDTO: TransactionDTO)
}
class UseCaseExampleImpl @Inject constructor(
private val baseSettingsPersistence: SettingsPersistence,
private val receiptPersistence: ReceiptPersistence,
private val transactionsPersistence: TransactionsPersistence,
) : DatabaseUseCase {
override suspend fun getShiftParams(): ShiftParamsDTO {
return baseSettingsPersistence.getShiftSettings()
}
override suspend fun getBaseSettings(): BaseSettingsDTO {
return baseSettingsPersistence.getBaseSettings()
}
override suspend fun getReceiptNumber(): Long {
return receiptPersistence.getCurrentReceiptNumber()
}
override suspend fun addTransaction(transaction: TransactionDTO) {
transactionsPersistence.add(transaction)
receiptPersistence.incrementReceiptNumber()
}
}
3. В Room-пакете имеется DI-модуль для Provides
@Module
class RoomModule {
@[Provides MainScreenScope]
fun providesAppDatabase(context: Context): AppDatabase = AppDatabase.getInstance(context)
@[Provides MainScreenScope]
fun provideBaseSettingsDao(appDatabase: AppDatabase): BaseSettingsDao =
appDatabase.baseSettingsDao()
/* other Dao */
}
4. In app-package AppComponent, AppComponentDependencies, AppModule, MainScreenComponent, MainScreenComponentDependencies, MainScreenModule, SubcomponentModule:
/**
* Main app component
*/
@Component(
modules = [
AppModule::class,
SubcomponentModule::class,
NetworkModule::class,
EvatorPrinterModule::class,
EvotorSDKModule::class,
],
dependencies = [AppComponentDependencies::class],
)
@AppScope
interface AppComponent : MainScreenComponentDependencies, P7LibComponentDependencies, NetworkComponentDependencies {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun appComponentDependencies(dependencies: AppComponentDependencies): Builder
fun build(): AppComponent
}
fun inject(application: App)
fun mainScreenComponentBuilder(): MainScreenComponent.Builder
}
interface AppComponentDependencies {
val context: Context
}
```
```
@Module
object AppModule {
@[Provides AppScope]
fun providesP7LibRepository(): IP7LibRepository = P7LibRepositoryImpl()
@[Provides AppScope]
fun provideUseCase(useCaseExampleImpl: UseCaseExampleImpl): DatabaseUseCase {
return useCaseExampleImpl
}
@[Provides AppScope]
fun providesP7LibCallbacks(useCaseExample: DatabaseUseCase, gatewayServerRepository: GatewayServerRepositoryApi): IP7LibCallbacks =
P7LibCallbacksImpl(useCaseExample, gatewayServerRepository)
@[Provides AppScope]
fun providesLogger(context: Context): ErrorLogger = FileLogger(context)
}
@MainScreenScope
@Subcomponent(
modules = [
RoomModule::class,
PersistenceModule::class,
MappersModule::class,
MainScreenModule::class,
],
)
interface MainScreenComponent {
/**
* Инициализация полей MainActivity
*/
fun inject(mainActivity: MainActivity)
/**
* Builder компоненты основного экрана
*/
@Subcomponent.Builder
interface Builder {
fun mainModule(module: MainScreenModule): Builder
fun roomModule(module: RoomModule): Builder
fun mappersModule(module: MappersModule): Builder
fun persistenceModule(module: PersistenceModule): Builder
fun build(): MainScreenComponent
}
}
/**
* Зависимости компоненты
*/
interface MainScreenComponentDependencies
@Module
class MainScreenModule {
@[Provides MainScreenScope]
fun provideConfigurationFileReader(context: Context): ConfigurationFileReader {
return ConfigurationFileReader(context)
}
@[Provides MainScreenScope]
fun providesCardReaderRepository(sdkRepository: ISDKRepository): CardReaderRepository =
object : CardReaderRepository {
override val sdkRepository: ISDKRepository
get() = sdkRepository
}
@[Provides MainScreenScope]
fun providesMainActivityViewModel(
repository: IP7LibRepository,
configurationFileReader: ConfigurationFileReader,
settingsPersistence: SettingsPersistence,
cardReader: CardReaderRepository,
callBacks: IP7LibCallbacks,
): MainActivityViewModel = MainActivityViewModel(
p7LibraryRepository = repository,
configurationFileReader = configurationFileReader,
settingsPersistence = settingsPersistence,
cardReaderRepository = cardReader,
p7LibCallbacks = callBacks,
)
@[Provides MainScreenScope]
fun provideNavHostController(
debitViewModelFactory: DebitViewModel.DebitViewModelFactory,
settingsViewModelFactory: SettingsViewModel.SettingsViewModelFactory,
): PosNavController {
return PosNavController(debitViewModelFactory, settingsViewModelFactory)
}
}
@Module(subcomponents = [MainScreenComponent::class])
object SubcomponentModule{}
5. App.kt:
class App : Application() {
private val applicationScope = CoroutineScope(SupervisorJob())
companion object {
/**
* Основная компонента приложения
*/
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
App.appComponent = DaggerAppComponent
.builder()
.application(this)
.appComponentDependencies(AppComponentDependenciesImpl())
.build()
appComponent.inject(this)
}
private inner class AppComponentDependenciesImpl : AppComponentDependencies {
override val context: Context = this@App
}
}
val Context.appComponent: AppComponent
get() = App.appComponent
6. MainActivity.kt:
class MainActivity : ComponentActivity() {
/**
* ViewModule основного экрана
*/
@Inject
lateinit var viewModel: MainActivityViewModel
@Inject
lateinit var debitViewModelAssistedFactory: DebitViewModel.DebitViewModelFactory
@Inject
lateinit var settingsViewModelAssistedFactory: SettingsViewModel.SettingsViewModelFactory
@Inject
lateinit var posNavController: PosNavController
lateinit var mainScreenSubcomponent: MainScreenComponent
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainScreenSubcomponent = App.appComponent.mainScreenComponentBuilder()
.mainModule(MainScreenModule())
.roomModule(RoomModule())
.mappersModule(MappersModule())
.persistenceModule(PersistenceModule())
.build()
mainScreenSubcomponent.inject(this)
setContent {
PetrolPlusTheme {
Surface {
// ui
}
}
}
}
}
PROBLEM:
C:\AppComponent.java:11: error: [Dagger/MissingBinding] ru.elka.prj.persitence.SettingsPersistence cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends ru.elka.prj.di.MainScreenComponentDependencies, ru.elka.prj.p7Lib.di.P7LibComponentDependencies, ru.elka.prj.network.di.NetworkComponentDependencies {
^
A binding for ru.elka.prj.persitence.SettingsPersistence exists in ru.elka.prj.di.MainScreenComponent:
ru.elka.prj.persitence.SettingsPersistence is injected at
[ru.elka.prj.di.AppComponent] ru.elka.prj.persitence.UseCaseExampleImpl(baseSettingsPersistence)
ru.elka.prj.persitence.UseCaseExampleImpl is injected at
[ru.elka.prj.di.AppComponent] ru.elka.prj.di.AppModule.provideUseCase(useCaseExampleImpl)
ru.elka.prj.persitence.DatabaseUseCase is injected at …