Search code examples
androidkotlinandroid-sqliteandroid-roomcoroutine

execute room db queries async using coroutine


what is the problem with the codes db queries freeze ui. every thing works , but freezing ui when running db query what is the point that i dont know . all queries make ui freeze, when executing Delete query , recycleview animation freezing ,but after commenting the line that execute query , recycleview work smoothly

hilt Module:

@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

@Provides
fun provideCardDao(passwordDatabase: PasswordDatabase): CardDao {
    return passwordDatabase.cardDao()
}

@Singleton 
@Provides
fun providesCoroutineScope(): CoroutineScope {
  
    return CoroutineScope(SupervisorJob() + Dispatchers.Default)
}

@Provides
@Singleton
fun providePasswordDatabase(
    @ApplicationContext appContext: Context,
    coroutineScope: CoroutineScope
): PasswordDatabase {
    return Room.databaseBuilder(
        appContext,
        PasswordDatabase::class.java,
        "mydb.db"
    )
        .addMigrations()
        .addCallback(MYDatabaseCallback(coroutineScope))
        .setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
        .fallbackToDestructiveMigration()
        .build()
}
}

card Dao

@Dao
interface CardDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCardDetails(cardDetails : BanksCardModel)


@Query("DELETE FROM CardDetailsTable WHERE id = :id")
suspend fun deleteCardDetails(id : Int)

@Query("SELECT * FROM CardDetailsTable WHERE id = :id")
 fun getOneCardDetails(id : Int):LiveData<BanksCardModel>

@Query("SELECT * FROM CardDetailsTable")
fun getAllCardDetails() : LiveData<List<BanksCardModel>>

@Query("SELECT * FROM CardDetailsTable WHERE username LIKE '%' || :str || '%' OR bank_name LIKE '%' || :str || '%' "
)
 fun queryOnCards(str:String): LiveData<List<BanksCardModel>>
}

password db

@Database(entities = [ BanksCardModel::class],version = 15,exportSchema = false)
abstract class PasswordDatabase : RoomDatabase() {
abstract fun cardDao() : CardDao
}

repository

@Singleton
class Repository @Inject constructor(private val cardDao: CardDao) {

suspend fun insertCardDetails(cardDetailsItem: BanksCardModel){
    cardDao.insertCardDetails(cardDetailsItem)
}
suspend fun deleteCardDetails(id : Int){
    cardDao.deleteCardDetails(id)
}
 fun getOneCardDetails(id : Int):LiveData<BanksCardModel>{
    return cardDao.getOneCardDetails(id)
}
fun getAllCardDetails() : LiveData<List<BanksCardModel>>{
    return cardDao.getAllCardDetails()
}
 fun queryOnCards(str : String) : LiveData<List<BanksCardModel>>{
    return cardDao.queryOnCards(str)
}
}

viewModel

@HiltViewModel
class DetailsViewModel @Inject constructor(
private val repository: Repository
) : ViewModel() {

private var cardDetailsList : LiveData<List<BanksCardModel>>


init {
    cardDetailsList = repository.getAllCardDetails() 
}


fun insertCardDetails(cardDetailsItem: BanksCardModel)= viewModelScope.launch {
    repository.insertCardDetails(cardDetailsItem)
}
 fun deleteCardDetails(id : Int)= viewModelScope.launch {
    repository.deleteCardDetails(id)
}
fun getOneCardDetails(id : Int):LiveData<BanksCardModel>{
   return repository.getOneCardDetails(id)
}
fun getAllCardDetails() : LiveData<List<BanksCardModel>>{
    return cardDetailsList
}

  fun queryOnCards(str:String) : LiveData<List<BanksCardModel>>{
         return repository.queryOnCards(str)
}

}

////edited

query in searchview

       override fun onQueryTextChange(newText: String?): Boolean {
            
            lifecycleScope.launch {
                    viewModel.queryOnCards(newText?:"").collect{
                        val searchedList = it.toMutableList()
                        showingEmptyListAnnounce(searchedList)
                        adapter.updateList(searchedList)
                    }
            }

            return true
        }

the update list function in onQueryTextChange -- its using DiffUtils

       fun updateList( updatedList : MutableList<BanksCardModel>){

           val callback = CustomCallback(mList, updatedList)
           val result  = DiffUtil.calculateDiff(callback)
           mList.clear()
           mList.addAll(updatedList)
         result.dispatchUpdatesTo(this)
 }

video of screen


Solution

  • LiveData is outdated. Please consider to use Kotlin Flow.

    Here is your use case:

    lifecycleScope.launch {
       dao.getData().collect { data ->
        //handle data here
       }
    }
    @Dao
    interface MyDao {
         @Query("SELECT * FROM somedata_table")
         fun getData(): Flow<List<SomeData>>
    }
    

    Reference

    There is a good Google codelab to practice Kotlin coroutine and Room queries

    Also please consider to use StrictMode - it will show you this and similar issues in future.