Thanks for reading, After some issues with Annotations after converting a Java App to Kotlin I have added the modules components qualifiers and Scopes and it builds after rebuilding it.But I'm stuck here as I want to use the DaggerFragmentComponent (Fragment Component) but it doesn't import as in it hasn't created that after rebuilding.
I only have access to DaggerCollections which is not want I want to execute the dependency injection....
FragmentModule:
package com.example.daggerappkotlin.di.module
import com.example.daggerappkotlin.di.qualifier.FragContext
import com.example.daggerappkotlin.ui.HomeFragment
import dagger.Module
import dagger.Provides
#Module
class FragmentModule(internal var fragment: HomeFragment) {
#FragContext
#Provides
internal fun provideFragment(): HomeFragment {
return fragment
}
#FragContext
#Provides
internal fun provideConnection(): String {
return "Connected Ben Mohammad"
}
}
FragmentComponent:
package com.example.daggerappkotlin.di.component
import android.content.Context
import com.example.daggerappkotlin.di.module.FragmentModule
import com.example.daggerappkotlin.di.qualifier.ApplicationContext
import com.example.daggerappkotlin.di.scope.FragmentScope
import com.example.daggerappkotlin.ui.HomeFragment
import dagger.Component
#FragmentScope
#Component(dependencies = [ApplicationComponent::class], modules = [FragmentModule::class])
interface FragmentComponent {
#ApplicationContext
val context: Context
fun inject(frag: HomeFragment)
}
ApplicationModule:
package com.example.daggerappkotlin.di.module
import android.content.Context
import com.example.daggerappkotlin.MyApplication
import com.example.daggerappkotlin.di.qualifier.ApplicationContext
import com.example.daggerappkotlin.di.qualifier.DatabaseInfo
import com.example.daggerappkotlin.di.qualifier.NetworkInfo
import dagger.Module
import dagger.Provides
#Module
class ApplicationModule(val application: MyApplication) {
#ApplicationContext
#Provides
internal fun provideContext(): Context {
return application
}
#Provides
#DatabaseInfo
internal fun provideDatabaseName(): String {
return "dummy_db"
}
#Provides
#DatabaseInfo
internal fun provideDatabaseVersion(): Int? {
return 1
}
#Provides
#NetworkInfo
internal fun provideApiKey(): String {
return "SOME_API_KEY"
}
}
ApplicationComponent:
package com.example.daggerappkotlin.di.component
import android.content.Context
import com.example.daggerappkotlin.MyApplication
import com.example.daggerappkotlin.data.local.DatabaseService
import com.example.daggerappkotlin.data.remote.NetworkService
import com.example.daggerappkotlin.di.module.ApplicationModule
import com.example.daggerappkotlin.di.qualifier.ApplicationContext
import dagger.Component
import javax.inject.Singleton
#Singleton
#Component(modules = [ApplicationModule::class])
interface ApplicationComponent {
fun inject(application: MyApplication)
#ApplicationContext
val context: Context
val networkService: NetworkService
val databaseService: DatabaseService
}
I am trying to do the building in MainActivity, HomeFragment and the MyApplication....
GithubRepo
Related
I'm trying to follow MVVM pattern to fetch data from the given api but getting error while Initiating a connection. My application gets crashed showing the error in the logcat.
My ModalClass.kt
package com.example.retrofitdemo2.api
class ModelClass : ArrayList<ModelClassItem>()
ModelClassItem:
package com.example.retrofitdemo2.api
data class ModelClassItem(
val body: String,
val id: Int,
val title: String,
val userId: Int
)
RetrofitHelperClass.kt
package com.example.retrofitdemo2.api
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitHelperClass {
companion object {
private val BASE_URL = "https://jsonplaceholder.typicode.com/"
var interceptor = HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BODY
}
var client = OkHttpClient.Builder().apply {
this.addInterceptor(interceptor)
}.build()
fun getInstance(): Retrofit {
return Retrofit.Builder().baseUrl(BASE_URL).client(client)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())).build()
}
}
}
RetrofitService.kt
package com.example.retrofitdemo2.api
import androidx.lifecycle.LiveData
import retrofit2.Response
import retrofit2.http.GET
interface RetrofitService {
#GET("/albums")
fun get(): LiveData<Response<ModelClass>>
}
Repository.kt: Here i'm getting errors which is mentioned at the end.
Seems like repository is unable to create Adapter call.enter code here
package com.example.retrofitdemo2.repository
import com.example.retrofitdemo2.api.RetrofitService
class Repositroy(retrofitService: RetrofitService) {
val response = retrofitService.get()
}
viewmodel.kt:
package com.example.retrofitdemo2.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.retrofitdemo2.api.ModelClass
import com.example.retrofitdemo2.api.ModelClassItem
import com.example.retrofitdemo2.repository.Repositroy
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainViewModel(repositroy: Repositroy) : ViewModel() {
var getdata = repositroy.response
}
MainViewModelFactory.kt:
package com.example.retrofitdemo2.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.retrofitdemo2.repository.Repositroy
class MainViewModelFactory(private val repositroy: Repositroy) :
ViewModelProvider.Factory
{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)){
return MainViewModel(repositroy) as T
}
throw IllegalArgumentException("Problem in View Model Factory")
}
}
MainActivity.kt:
package com.example.retrofitdemo2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.retrofitdemo2.api.RetrofitHelperClass
import com.example.retrofitdemo2.api.RetrofitService
import com.example.retrofitdemo2.databinding.ActivityMainBinding
import com.example.retrofitdemo2.repository.Repositroy
import com.example.retrofitdemo2.viewmodel.MainViewModel
import com.example.retrofitdemo2.viewmodel.MainViewModelFactory
import retrofit2.Retrofit
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewmodel : MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
var retorservice =
RetrofitHelperClass.getInstance().create(RetrofitService::class.java)
val repositroy = Repositroy(retorservice)
val factory = MainViewModelFactory(repositroy)
viewmodel = ViewModelProvider(this, factory).get(MainViewModel::class.java)
binding.viewmodel = viewmodel
binding.lifecycleOwner = this
viewmodel.getdata.observe(this, Observer {
Log.i("MainActivity", "${it}")
})
}
}
Error:
Unable to start activity
ComponentInfo{com.example.retrofitdemo2/com.example.retrofitdemo2.MainActivity}:
java.lang.IllegalArgumentException: Unable to create call adapter for
androidx.lifecycle.LiveData<retrofit2.Response<com.example.retrof
at com.example.retrofitdemo2.repository.Repositroy.<init>(Repositroy.kt:7)
at com.example.retrofitdemo2.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:8109)
at android.app.Activity.performCreate(Activity.java:8083)
I’ve been working on upgrading our Android project from Android Studio 3.0.1 to 4.0.1, and as part of that, I have upgraded the following:
Build Tools Version 26.0.2 -> 30.0.2
Compile SDK Version 25 -> 28
Kotlin version 1.2.71 -> 1.4.10
Gradle version 3.1.0 -> 4.0.1
Android Support v7 -> androidx 1.0.0
Dagger 2.9 -> 2.28.3
Retrofit 2.2.0 -> 2.9.0
Okhttp 3.7.0 -> 4.9.0
I am now running into an issue where I am getting an error saying the following:
#dagger.Component(dependencies = {org.bbb.ccc.app.application.AppComponent.class}, modules = {org.bbb.ccc.app.ddd.yyy.zzz.class})
^
#Singleton org.bbb.ccc.app.application.AppComponent/Users/xxx/aaa_android/app/build/tmp/kapt3/stubs/eee/org/xxx/ddd/app/application/AppComponent.java:8: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends error.NonExistentClass>,javax.inject.Provider<org.bbb.ccc.app.dagger.fragment.FragmentComponentBuilder<?,?>>> cannot be provided without an #Provides-annotated method.
So far, as part of this migration, I have moved several classes from being a Singleton to their own scope, as discussed in Singleton component cannot depend on scoped components
I have also tried to follow the information here: http://frogermcs.github.io/activities-multibinding-in-dagger-2/ as well as a few other things, and I have not yet been able to get the build to compile with AS4. This project compiled in 3.0.1 before upgrading.
Onto code now:
App.kt:
package org.bbb.ccc.app.application
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.StrictMode
import android.util.Log
import com.crashlytics.android.Crashlytics
import io.fabric.sdk.android.Fabric
import io.realm.Realm
import io.realm.RealmConfiguration
import org.bbb.ccc.app.dagger.activity.ActivityComponentBuilder
import org.bbb.ccc.app.dagger.activity.HasActivitySubcomponentBuilders
import org.bbb.ccc.app.data.migration.KäsefüßeRealmMigration
import org.bbb.ccc.app.data.repository.RealmDatabase
import org.bbb.ccc.app.utils.logouttimer.LogoutTimerService
import javax.inject.Inject
/**
* Application Class (Debug Version)
* #desc - Instantiate a Base class for all Android Activities and Services. Also creates global
* objects for Realm, Dagger, Analytics, Memory Management, etc.
*/
open class App : Application(), HasActivitySubcomponentBuilders {
companion object {
var context: Context? = null
}
#Inject
lateinit var activityComponentBuilders: MutableMap<Class<out Activity?>?, #JvmSuppressWildcards ActivityComponentBuilder<*, *>?>
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
context = this
// Set Crashlytics for Firebase
Fabric.with(this, Crashlytics())
createRealm("KäsefüßeDebug.Realm", 4L)
appComponent = createAppComponent()
appComponent.inject(this)
try{
Intent(this, LogoutTimerService::class.java).also { intent ->
startService(intent)
}
} catch (e: IllegalStateException){
Log.d("Käsefüße", e.toString())
}
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
override fun onTerminate() {
LogoutTimerService.sharedInstance.cancelTimer()
Intent(this, LogoutTimerService::class.java).also { intent ->
stopService(intent)
}
super.onTerminate()
}
protected open fun createAppComponent() : AppComponent {
return DaggerAppComponent.builder()
.appModule(AppModule(this))
.apiModule(ApiModule())
.dbModule(DbModule())
.build()
}
protected open fun createRealm(realmName: String, realmVersion: Long) {
Realm.init(this)
RealmDatabase.realmConfiguration = RealmConfiguration.Builder()
.name(realmName)
.schemaVersion(realmVersion)
.migration(KäsefüßeRealmMigration())
.build()
}
override fun getBuilder(activityClass: Class<out Activity?>?): ActivityComponentBuilder<*, *>? {
return activityComponentBuilders[activityClass]
}
}
AppComponent.kt:
package org.bbb.ccc.app.application
import android.content.Context
import dagger.Component
import org.bbb.ccc.app.vol.ComplimenteuseService
import org.bbb.ccc.app.dagger.activity.ActivityBindingModule
import org.bbb.ccc.app.data.preference.PreferenceFactory
import org.bbb.ccc.app.Fromage.FromageService
import org.bbb.ccc.app.nourriture.Ananas.AnanasActivity
import org.bbb.ccc.app.nourriture.pommeDuTerre.PommeDuTerreActivity
import javax.inject.Singleton
#Singleton
#Component(modules = [AppModule::class, ActivityBindingModule::class])
interface AppComponent {
fun inject(app: App)
fun inject(FromageService: FromageService)
fun inject(complimenteuseService: ComplimenteuseService)
fun context() : Context
fun preferenceFactory() : PreferenceFactory
fun inject(ananasActivity: AnanasActivity)
fun inject(pommeDuTerreActivity: PommeDuTerreActivity)
}
AppModule.kt:
package org.bbb.ccc.app.application
import android.content.Context
import android.content.SharedPreferences
import com.google.gson.Gson
import dagger.Module
import dagger.Provides
import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.bbb.ccc.app.bluetooth.BTUtils
import org.bbb.ccc.app.data.preference.PreferenceFactory
import org.bbb.ccc.app.domain.model.Profile
import org.bbb.ccc.app.domain.network.KäsefüßeAPI
import org.bbb.ccc.app.domain.repository.ComplimenteuseRepository
import org.bbb.ccc.app.domain.repository.TrainRepository
import org.bbb.ccc.app.domain.repository.UserRepository
import org.bbb.ccc.app.login.interactor.ProfileInteractor
import org.bbb.ccc.app.login.interactor.ProfileManager
import org.bbb.ccc.app.presentation.interactor.SchedulerProvider
import org.bbb.ccc.app.utils.android.DeviceNameGenerator
import org.bbb.ccc.app.utils.android.HasConnection
import org.bbb.ccc.app.utils.connectionstate.ConnectionBroadCastReceiverRegistry
import org.bbb.ccc.app.utils.connectionstate.ConnectionNotifier
import org.bbb.ccc.app.utils.connectionstate.OkHttpPingService
import org.bbb.ccc.app.utils.extensions.hasInternetConnection
import javax.inject.Singleton
/**
* App Module - Dagger.
*/
#Module(includes = [ApiModule::class, DbModule::class])
class AppModule(private val application: App) {
#Provides
#Singleton
fun app(): App = application
#Provides
#Singleton
fun sharedPreferences(): SharedPreferences =
application.getSharedPreferences("PREFS", Context.MODE_PRIVATE)
#Provides
#Singleton
fun applicationContext() : Context = application
#Provides
#Singleton
fun schedulerProvider() : SchedulerProvider {
return object : SchedulerProvider {
override fun ui(): Scheduler = AndroidSchedulers.mainThread()
override fun computation(): Scheduler = Schedulers.computation()
override fun trampoline(): Scheduler = Schedulers.trampoline()
override fun newThread(): Scheduler = Schedulers.newThread()
override fun io(): Scheduler = Schedulers.io()
}
}
#Provides
#Singleton
fun preferencesManager(sharedPreferences: SharedPreferences, gson: Gson) : PreferenceFactory =
PreferenceFactory(sharedPreferences, gson)
#Provides
#Singleton
fun profileInteractor(preferenceFactory: PreferenceFactory, trainRepository: TrainRepository,
userRepository: UserRepository, schedulerProvider: SchedulerProvider)
: ProfileManager {
val preference = preferenceFactory.create<Profile>(PreferenceFactory.PreferenceKey.PROFILE)
return ProfileInteractor(preference, trainRepository, userRepository, schedulerProvider)
}
#Provides
#Singleton
fun hasConnection() : HasConnection {
return object : HasConnection {
override fun available(): Boolean = application.hasInternetConnection()
}
}
#Provides
#Singleton
fun connectionNotifier(context: Context, hasConnection: HasConnection, schedulerProvider: SchedulerProvider)
: ConnectionNotifier =
ConnectionNotifier(OkHttpPingService(), ConnectionBroadCastReceiverRegistry(context), hasConnection, schedulerProvider)
#Provides
#Singleton
fun ComplimenteuseInteractor(ComplimenteuseRepository: ComplimenteuseRepository,
KäsefüßeAPI: KäsefüßeAPI,
hasConnection: HasConnection,
preferenceFactory: PreferenceFactory,
schedulerProvider: SchedulerProvider): ComplimenteuseManager {
val preference = preferenceFactory.create<Profile>(PreferenceFactory.PreferenceKey.PROFILE)
val deviceNameGenerator = object : DeviceNameGenerator {
override fun name(): String = BTUtils.deviceName
}
return ComplimenteuseInteractor(ComplimenteuseRepository, KäsefüßeAPI, hasConnection, preference, deviceNameGenerator, schedulerProvider)
}
}
ActivityBindingModule:
package org.bbb.ccc.app.dagger.activity
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import org.bbb.ccc.app.vol.AeroplanActivity
import org.bbb.ccc.app.vol.AeroplanComponent
import org.bbb.ccc.app.login.LoginActivity
import org.bbb.ccc.app.login.LoginComponent
import org.bbb.ccc.app.nourriture.viande.BoeufActivity
import org.bbb.ccc.app.nourriture.viande.BoeufComponent
import org.bbb.ccc.app.nourriture.fruit.PommeComponent
import org.bbb.ccc.app.nourriture.fruit.CarambolaActivity
import org.bbb.ccc.app.nourriture.fruit.RaisinActivity
import org.bbb.ccc.app.nourriture.fruit.RaisinComponent
import org.bbb.ccc.app.KäsefüßeKäse.KäseActivity
import org.bbb.ccc.app.KäsefüßeKäse.KäseComponent
/**
* Created by Bob Jones on 4/26/17.
*/
#Module(
subcomponents = [LoginComponent::class, AeroplanComponent::class, KäseComponent::class, PommeComponent::class, BoeufComponent::class, RaisinComponent::class]
)
abstract class ActivityBindingModule {
#Binds
#IntoMap
#ActivityKey(LoginActivity::class)
abstract fun loginComponentBuilder(impl: LoginComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(AeroplanActivity::class)
abstract fun AeroplanComponentBuilder(impl: AeroplanComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(KäseActivity::class)
abstract fun KäseActivityComponentBuilder(impl: KäseComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(CarambolaActivity::class)
abstract fun carambolaComponentBuilder(impl: PommeComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(BoeufActivity::class)
abstract fun boeufActivity(impl: BoeufComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(RaisinActivity::class)
abstract fun RaisinComponentBuilder(impl: RaisinComponent.Builder) : ActivityComponentBuilder<*, *>
}
Thanks in advance for your assistance!
It says the FragmentComponentBuilder is missing code - I don't see where you create the FragmentComponentBuilder in your post so maybe start looking there and checking that all your fragments are being properly injected.
I am new to dagger 2. I was making a CarComponent on kotlin, I was trying to call my DaggerCarComponent with horsePower value without calling petrolEngineModule. the following is my code :
import dagger.BindsInstance
import dagger.Component
import javax.inject.Singleton
#Component (
modules = [WheelModule::class, PetrolEngineModule::class]
)
interface CarComponent {
fun getCar(): Car
fun inject(mainActivity: MainActivity)
#Component.Builder
interface Builder {
#BindsInstance
fun horsePower(horsePower : Int) : Builder
fun build(): CarComponent
}
}
this is PetrolEngine.kt:
package com.example.daggerapp
import android.util.Log
import javax.inject.Inject
class PetrolEngine : Engine {
private var horsePower : Int
#Inject constructor(horsePower: Int){
this.horsePower = horsePower
}
override fun start() {
Log.d("Engine", "Broom..., horsePower: ${this.horsePower}")
}
}
this is PetrolEngineModule.kt:
package com.example.daggerapp
import dagger.Module
import dagger.Provides
import javax.inject.Inject
#Module
class PetrolEngineModule {
private var horsePower: Int
#Inject constructor(horsePower: Int) {
this.horsePower = horsePower
}
#Provides
fun provideHorsePower(): Int {
return horsePower
}
#Provides
fun provideEngine(engine: PetrolEngine): Engine
{
return engine
}
}
I added the DaggerComponent here as DaggerCarComponent :
package com.example.daggerapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
#Inject
lateinit var car:Car
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val daggerCar: CarComponent = DaggerCarComponent.builder().petrolEngineModule(PetrolEngineModule(140)).build()
daggerCar.inject(this)
Log.d("Car instance", "$car")
car.drive()
}
}
I was following this tutorial: https://www.youtube.com/watch?v=3tIvekCTSJg&list=PLrnPJCHvNZuA2ioi4soDZKz8euUQnJW65&index=8
In your Builder:
#BindsInstance
Builder horsePower(#Named("horse_power") int horsePower);
After this you will be able to pass horsePower from MainActivity without passing instance of PetrolEngineModule.And same way in ur PetrolEngine constructor:
#Inject
public PetrolEngine(#Named("horse_power") int horsePower) {
this.horsePower = horsePower;
}
And in your PetrolEngineModule u can remove everything and just leave #Provides for PetrolEngine.
Remove #Inject in the module class, cause that what #BindsInstance is doing when pass horsePower it in #Component.Builder
Just an optimization for future viewers. Just replace the existing code with this. Now Dagger will not create the --Provide-- class to provide the instance of PetrolEngine.
Please improve the code if you find anything wrong.
#Module
abstract class PetrolEngineModule {
#Binds
abstract fun bindEngine(engine: PetrolEngine): Engine
}
I am trying to set dagger up to provide a data module but I keep running into this error :
error: #Modules cannot be scoped. Did you mean to scope a method instead?
#javax.inject.Singleton()
^
Here is the culprit module :
package com.bottlerocket.dependancyinjection.modules
import android.content.Context
import androidx.room.Room
import com.bottlerocket.dependancyinjection.DI
import com.data.api.BottleRocketApi
import com.data.cache.StoreCache
import com.data.database.CacheDataStoreObject
import com.data.database.RemoteStoreDataObject
import com.data.database.StoreDatabase
import com.data.repository.StoreRepositoryImplementation
import dagger.Module
import dagger.Provides
import interfaces.StoresRepository
import javax.inject.Named
import javax.inject.Singleton
#Module
#Singleton
class DataModule {
#Singleton
#Provides
fun provideRoomDatabase(context: Context): StoreDatabase {
return Room.databaseBuilder(
context,
StoreDatabase::class.java,
"stores_db"
).build()
}
#Provides
#Singleton
fun provideStoreRepository(
api: BottleRocketApi,
#Named(DI.inMemoryCache) cache: StoreCache
): StoresRepository {
val cachedMoviesDataStore = CacheDataStoreObject(cache)
val remoteMoviesDataStore = RemoteStoreDataObject(api)
return StoreRepositoryImplementation(cachedMoviesDataStore, remoteMoviesDataStore)
}
}
As the error indicates, you can't annotate a module with #Singleton, only the functions. So all you have to do is remove the annotation from your module.
Change
#Module
#Singleton
class DataModule {
}
with
#Module
class DataModule {
}
This check was introduced in a recent version of Dagger.
I want to pre-populate my Room database from the json file in the assets folder. I follow the Google Sunflower sample. I copied the SeedDatabaseWorker class:
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.dmitrysimakov.gymlab.data.GymLabDb
import com.dmitrysimakov.gymlab.data.entity.Training
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import javax.inject.Inject
class SeedDatabaseWorker(val context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
private val TAG = SeedDatabaseWorker::class.java.simpleName
#Inject lateinit var database: GymLabDb
override fun doWork(): Worker.Result {
val plantType = object : TypeToken<List<Training>>() {}.type
var jsonReader: JsonReader? = null
return try {
val inputStream = context.assets.open("training.json")
jsonReader = JsonReader(inputStream.reader())
val plantList: List<Training> = Gson().fromJson(jsonReader, plantType)
database.trainingDao().insert(plantList)
Worker.Result.SUCCESS
} catch (ex: Exception) {
Log.e(TAG, "Error seeding database", ex)
Worker.Result.FAILURE
} finally {
jsonReader?.close()
}
}
}
I'm using Dagger 2, so instead of doing this: Sunflower AppDatabase, I do this:
import android.arch.persistence.db.SupportSQLiteDatabase
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.dmitrysimakov.gymlab.GymLabApp
import com.dmitrysimakov.gymlab.data.GymLabDb
import com.dmitrysimakov.gymlab.workers.SeedDatabaseWorker
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
#Module(includes = [ViewModelModule::class])
class AppModule {
#Singleton
#Provides
fun provideDb(app: GymLabApp): GymLabDb {
return Room
.databaseBuilder(app, GymLabDb::class.java, "gymlab.db")
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance().enqueue(request)
}
})
.fallbackToDestructiveMigration()
.build()
}
#Singleton
#Provides
fun provideTrainingDao(db: GymLabDb) = db.trainingDao()
}
But I can't inject the database that has not yet been created. So, how can I access the dao?
The problem was that I couldn't inject my database into the Worker. I found the solution here: AndroidWorkerInjection
Your issue is that SeedDatabaseWorker is still based on Worker() which is deprecated now, so you need to use Worker(Context, WorkerParameters) this constructor.
Check my answer from another post, it'll help you understand WorkManager library.
Edit :
You can now check Worker from that Sunflower demo, it's updated.