Home > OS >  Android Room - Trying to query a single row based on Primary ID
Android Room - Trying to query a single row based on Primary ID

Time:06-11

What am I trying to achieve ?

Get a single row of data which has the id I need. The SQL equivalent of SELECT * FROM favs WHERE link='link'. I have written a fun named getOneFav() which for this. I am following the tutorial https://developer.android.com/codelabs/android-room-with-a-view-kotlin#0 and code from https://github.com/android/sunflower

What have I setup so far ?

Entity

@Entity(tableName = "favs")
data class Favorite(
    @PrimaryKey @ColumnInfo(name = "link") val link : String,
    @ColumnInfo(name = "keywords") val keywords : String
)

DAO

@Dao
interface FavDAO {
    @Query("SELECT * FROM favs")
    fun getAllFavsLive(): Flow<List<Favorite>>

    @Query("SELECT * FROM favs WHERE link = :link")
    fun getOneFav(link: String): Favorite

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(link: Favorite)
}

Repository

class FavRepo (private val favDao: FavDAO) {

    val allFavs: Flow<List<Favorite>> = favDao.getAllFavsLive()

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun insert(link: Favorite) {
        favDao.insert(link)
    }

    fun getOneFav(link: String) = favDao.getOneFav(link)

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun delete(link: String) {
        favDao.delete(link)
    }
}

ViewModel

class FavViewModel (private val repository: FavRepo) : ViewModel() {

    val allFavs: LiveData<List<Favorite>> = repository.allFavs.asLiveData()

    fun insert(link: Favorite) = viewModelScope.launch {
        repository.insert(link)
    }

    fun getOneFav(link: String) = repository.getOneFav(link)

}

class FavViewModelFactory(private val repository: FavRepo) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(FavViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return FavViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

What problems am I facing ?

I am receiving an error saying

java.lang.RuntimeException: Unable to start activity ComponentInfo{[package name removed].MainActivity}: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

What have I tried so far ?

I have tried -

  • Adding suspend in front of the function getOneFav in DAO and Repository
  • Made the function run inside viewModelScope. It gave the same error as above. Also, this way the function returned a Job instead of the 'Favorite' data class object.
    fun getOneFav(link: String) = viewModelScope.launch {
        repository.getOneFav(link)
    }
  • Followed this method here - How to implement a Room LiveData filter which even though worked, which seemed like an overkill for something so simple. Also despite the fact that the code is using MutableLiveData, I wasn't able to see any triggers when the insert happened.

CodePudding user response:

in your Db try adding .fallbackToDestructiveMigration().allowMainThreadQueries().build() after the room database builder to put that ANR check

CodePudding user response:

You should run your queries in a different context:

class FavRepo (private val favDao: FavDAO) {

    val allFavs: Flow<List<Favorite>> = withContext(Dispatchers.IO) { 
        favDao.getAllFavsLive()
    }

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun insert(link: Favorite) = withContext(Dispatchers.IO) {
        favDao.insert(link)
    }

    fun getOneFav(link: String) = withContext(Dispatchers.IO) { 
        favDao.getOneFav(link)
    }

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun delete(link: String) = withContext(Dispatchers.IO) {
        favDao.delete(link)
    }
}
  • Related