all: inject room dbs

Inject room databases instead of lazily creating them.

This should make repositories much easier to test.
This commit is contained in:
Alexander Capehart 2023-02-12 19:15:11 -07:00
parent dd2017c510
commit e017d53139
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 49 additions and 86 deletions

View file

@ -17,14 +17,12 @@
package org.oxycblt.auxio.music.cache package org.oxycblt.auxio.music.cache
import android.content.Context
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Database import androidx.room.Database
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Insert import androidx.room.Insert
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.Query import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverter import androidx.room.TypeConverter
import androidx.room.TypeConverters import androidx.room.TypeConverters
@ -36,35 +34,6 @@ import org.oxycblt.auxio.music.model.RawSong
@Database(entities = [CachedSong::class], version = 27, exportSchema = false) @Database(entities = [CachedSong::class], version = 27, exportSchema = false)
abstract class CacheDatabase : RoomDatabase() { abstract class CacheDatabase : RoomDatabase() {
abstract fun cachedSongsDao(): CachedSongsDao abstract fun cachedSongsDao(): CachedSongsDao
companion object {
@Volatile private var INSTANCE: CacheDatabase? = null
/**
* Get/create the shared instance of this database.
* @param context [Context] required.
*/
fun getInstance(context: Context): CacheDatabase {
val instance = INSTANCE
if (instance != null) {
return instance
}
synchronized(this) {
val newInstance =
Room.databaseBuilder(
context.applicationContext,
CacheDatabase::class.java,
"auxio_tag_cache.db")
.fallbackToDestructiveMigration()
.fallbackToDestructiveMigrationFrom(0)
.fallbackToDestructiveMigrationOnDowngrade()
.build()
INSTANCE = newInstance
return newInstance
}
}
}
} }
@Dao @Dao

View file

@ -17,10 +17,15 @@
package org.oxycblt.auxio.music.cache package org.oxycblt.auxio.music.cache
import android.content.Context
import androidx.room.Room
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import org.oxycblt.auxio.music.extractor.CacheRepository import org.oxycblt.auxio.music.extractor.CacheRepository
import org.oxycblt.auxio.music.extractor.CacheRepositoryImpl import org.oxycblt.auxio.music.extractor.CacheRepositoryImpl
@ -29,3 +34,19 @@ import org.oxycblt.auxio.music.extractor.CacheRepositoryImpl
interface CacheModule { interface CacheModule {
@Binds fun cacheRepository(cacheRepository: CacheRepositoryImpl): CacheRepository @Binds fun cacheRepository(cacheRepository: CacheRepositoryImpl): CacheRepository
} }
@Module
@InstallIn(SingletonComponent::class)
class CacheRoomModule {
@Singleton
@Provides
fun database(@ApplicationContext context: Context) =
Room.databaseBuilder(
context.applicationContext, CacheDatabase::class.java, "music_cache.db")
.fallbackToDestructiveMigration()
.fallbackToDestructiveMigrationFrom(0)
.fallbackToDestructiveMigrationOnDowngrade()
.build()
@Provides fun cachedSongsDao(database: CacheDatabase) = database.cachedSongsDao()
}

View file

@ -17,10 +17,7 @@
package org.oxycblt.auxio.music.extractor package org.oxycblt.auxio.music.extractor
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.music.cache.CacheDatabase
import org.oxycblt.auxio.music.cache.CachedSong import org.oxycblt.auxio.music.cache.CachedSong
import org.oxycblt.auxio.music.cache.CachedSongsDao import org.oxycblt.auxio.music.cache.CachedSongsDao
import org.oxycblt.auxio.music.model.RawSong import org.oxycblt.auxio.music.model.RawSong
@ -44,12 +41,8 @@ interface CacheRepository {
suspend fun writeCache(rawSongs: List<RawSong>) suspend fun writeCache(rawSongs: List<RawSong>)
} }
class CacheRepositoryImpl @Inject constructor(@ApplicationContext private val context: Context) : class CacheRepositoryImpl @Inject constructor(private val cachedSongsDao: CachedSongsDao) :
CacheRepository { CacheRepository {
private val cachedSongsDao: CachedSongsDao by lazy {
CacheDatabase.getInstance(context).cachedSongsDao()
}
override suspend fun readCache(): Cache? = override suspend fun readCache(): Cache? =
try { try {
// Faster to load the whole database into memory than do a query on each // Faster to load the whole database into memory than do a query on each

View file

@ -17,7 +17,6 @@
package org.oxycblt.auxio.playback.persist package org.oxycblt.auxio.playback.persist
import android.content.Context
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Database import androidx.room.Database
import androidx.room.Entity import androidx.room.Entity
@ -25,7 +24,6 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.Query import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverter import androidx.room.TypeConverter
import androidx.room.TypeConverters import androidx.room.TypeConverters
@ -61,35 +59,6 @@ abstract class PersistenceDatabase : RoomDatabase() {
/** @see [Music.UID.fromString] */ /** @see [Music.UID.fromString] */
@TypeConverter fun toMusicUid(string: String?) = string?.let(Music.UID::fromString) @TypeConverter fun toMusicUid(string: String?) = string?.let(Music.UID::fromString)
} }
companion object {
@Volatile private var INSTANCE: PersistenceDatabase? = null
/**
* Get/create the shared instance of this database.
* @param context [Context] required.
*/
fun getInstance(context: Context): PersistenceDatabase {
val instance = INSTANCE
if (instance != null) {
return instance
}
synchronized(this) {
val newInstance =
Room.databaseBuilder(
context.applicationContext,
PersistenceDatabase::class.java,
"auxio_playback_persistence.db")
.fallbackToDestructiveMigration()
.fallbackToDestructiveMigrationFrom(1)
.fallbackToDestructiveMigrationOnDowngrade()
.build()
INSTANCE = newInstance
return newInstance
}
}
}
} }
/** /**

View file

@ -17,13 +17,38 @@
package org.oxycblt.auxio.playback.persist package org.oxycblt.auxio.playback.persist
import android.content.Context
import androidx.room.Room
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
interface PersistenceModule { interface PersistenceModule {
@Binds fun repository(persistenceRepository: PersistenceRepositoryImpl): PersistenceRepository @Binds fun repository(persistenceRepository: PersistenceRepositoryImpl): PersistenceRepository
} }
@Module
@InstallIn(SingletonComponent::class)
class PersistenceRoomModule {
@Singleton
@Provides
fun database(@ApplicationContext context: Context) =
Room.databaseBuilder(
context.applicationContext,
PersistenceDatabase::class.java,
"playback_persistence.db")
.fallbackToDestructiveMigration()
.fallbackToDestructiveMigrationFrom(1)
.fallbackToDestructiveMigrationOnDowngrade()
.build()
@Provides fun playbackStateDao(database: PersistenceDatabase) = database.playbackStateDao()
@Provides fun queueDao(database: PersistenceDatabase) = database.queueDao()
}

View file

@ -17,8 +17,6 @@
package org.oxycblt.auxio.playback.persist package org.oxycblt.auxio.playback.persist
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.model.Library import org.oxycblt.auxio.music.model.Library
@ -43,22 +41,12 @@ interface PersistenceRepository {
* @param state The [PlaybackStateManager.SavedState] to persist. * @param state The [PlaybackStateManager.SavedState] to persist.
*/ */
suspend fun saveState(state: PlaybackStateManager.SavedState?): Boolean suspend fun saveState(state: PlaybackStateManager.SavedState?): Boolean
companion object {
/**
* Get a framework-backed implementation.
* @param context [Context] required.
*/
fun from(context: Context): PersistenceRepository = PersistenceRepositoryImpl(context)
}
} }
class PersistenceRepositoryImpl class PersistenceRepositoryImpl
@Inject @Inject
constructor(@ApplicationContext private val context: Context) : PersistenceRepository { constructor(private val playbackStateDao: PlaybackStateDao, private val queueDao: QueueDao) :
private val database: PersistenceDatabase by lazy { PersistenceDatabase.getInstance(context) } PersistenceRepository {
private val playbackStateDao: PlaybackStateDao by lazy { database.playbackStateDao() }
private val queueDao: QueueDao by lazy { database.queueDao() }
override suspend fun readState(library: Library): PlaybackStateManager.SavedState? { override suspend fun readState(library: Library): PlaybackStateManager.SavedState? {
val playbackState: PlaybackState val playbackState: PlaybackState

View file

@ -146,8 +146,6 @@ class PlaybackService :
.build() .build()
.also { it.addListener(this) } .also { it.addListener(this) }
replayGainProcessor.addToListeners(player) replayGainProcessor.addToListeners(player)
// Initialize the core service components
persistenceRepository = PersistenceRepository.from(this)
foregroundManager = ForegroundManager(this) foregroundManager = ForegroundManager(this)
// Initialize any listener-dependent components last as we wouldn't want a listener race // Initialize any listener-dependent components last as we wouldn't want a listener race
// condition to cause us to load music before we were fully initialize. // condition to cause us to load music before we were fully initialize.