Я пытаюсь использовать Dagger 2 в проекте ViewModel + Respository + Room + Retrofit + Coroutines, написанном в Kotlin.
В настоящее время каждый ViewModel инициализирует требуемые репозитории и их зависимости, например, так:
class HomeViewModel(
application: Application
) : AndroidViewModel(application) {
private val repository: UserRepository =
UserRepository(
Webservice.create(),
AppDatabase.getDatabase(application, viewModelScope).userDao()
)
Я бы хотел упростить это до этого
class HomeViewModel @Inject constructor(
private val repository: UserRepository
): ViewModel()
Чего я достиг к настоящему моменту
Создание компонента и модулей кинжала
@Singleton
@Component(modules = [
AppModule::class,
NetworkModule::class,
DataModule::class,
RepositoryModule::class
])
interface AppComponent {
val webservice: Webservice
val userRepository: UserRepository
}
@Module
class AppModule(private val app: Application) {
@Provides
@Singleton
fun provideApplication(): Application = app
}
@Module
class DataModule {
@Provides
@Singleton
fun provideApplicationDatabase(app: Application, scope: CoroutineScope) =
AppDatabase.getDatabase(app, scope)
@Provides
@Singleton
fun provideUserDao(db: AppDatabase) = db.userDao()
}
@Module
class NetworkModule {
@Provides
@Singleton
fun provideWebservice() = Webservice.create()
}
@Module
class RepositoryModule {
@Provides
@Singleton
fun provideUserRepository(webservice: Webservice, userDao: UserDao) =
UserRepository(webservice, userDao)
}
Получил AppComponent, инициализированный в классе приложения
class App : Application() {
companion object {
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
appComponent = initDagger(this)
}
private fun initDagger(app: App): AppComponent =
DaggerAppComponent.builder()
.appModule(AppModule(app))
.build()
}
И теперь я застрял.
Первый вопрос : Как мне сделать конструктор инъекции ViewModel работа?
Первоначально он был инициализирован из HomeFragment примерно так:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
Как мне теперь вызвать инициализатор?
Второй вопрос немного сложнее.
Конструктор базы данных требует область действия Coroutine, чтобы предварительно заполнить ее в фоновом потоке во время создания. Как мне передать область сейчас?
Вот определение базы данных и обратный вызов
@Database(
entities = [User::class],
version = 1, exportSchema = false
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context, scope: CoroutineScope): AppDatabase {
val tempInstance =
INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"database"
)
.fallbackToDestructiveMigration()
.addCallback(AppDatabaseCallback(scope))
.build()
INSTANCE = instance
return instance
}
}
}
private class AppDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
INSTANCE?.let { database ->
scope.launch(Dispatchers.IO) {
//inserts
}
}
}
}
}