I am a total beginner with kotlin and android development. I followed Persist data with Room tutorial and I have a popular problem to save my data:
Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
I understand why by reading other responses. But I don't understand how to deal with it.
My entity and its DAO :
@Entity
data class AccountConfiguration(
@PrimaryKey val server_address: String,
@ColumnInfo(name = "user_name") val user_name: String,
@ColumnInfo(name = "password") val password: String, // FIXME : secure storage
@ColumnInfo(name = "notify_hungry") val notify_hungry: Boolean,
@ColumnInfo(name = "notify_thirsty") val notify_thirsty: Boolean,
@ColumnInfo(name = "notify_ap") val notify_ap: Boolean,
@ColumnInfo(name = "network_grab_each") val network_grab_each: Int,
)
@Dao
interface AccountConfigurationDao {
@Query("SELECT * FROM accountconfiguration LIMIT 1")
fun get(): Flow<AccountConfiguration>
@Query("DELETE FROM accountconfiguration")
fun clear()
@Insert
fun insert(account_configuration: AccountConfiguration)
}
Its view model :
class AccountConfigurationViewModel(private val repository: AccountConfigurationRepository) : ViewModel() {
val accountConfiguration: LiveData<AccountConfiguration> = repository.accountConfiguration.asLiveData()
fun insert(account_configuration: AccountConfiguration) = viewModelScope.launch {
repository.update(account_configuration)
}
fun isEntryValid(server_address: String, user_name: String, password: String, network_grab_each: Int): Boolean {
if (server_address.isBlank() || user_name.isBlank() || password.isBlank() || network_grab_each < 5 * 60) {
return false
}
return true
}
}
The database and Application.
Then, concerned part of the view (entire file here) :
class AccountConfigurationFragment : Fragment() {
private var _binding: AccountConfigurationFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private val viewModel: AccountConfigurationViewModel by activityViewModels {
AccountConfigurationViewModelFactory(
(activity?.application as RollingDashboardApplication).account_configuration_repository
)
}
lateinit var accountConfiguration: AccountConfiguration
// [...] hidden code
private fun save() {
Toast.makeText(context, R.string.saving, Toast.LENGTH_LONG).show()
if (isEntryValid()) {
val networkGrabEach = 3600 // FIXME : determine value for real
viewModel.insert(
AccountConfiguration(
server_address = binding.textInputServerAddress.text.toString(),
// [...] hidden code
network_grab_each = networkGrabEach,
)
)
Toast.makeText(context, R.string.account_configuration_saved, Toast.LENGTH_LONG).show()
findNavController().navigate(R.id.action_AccountConfigurationFragment_to_DashboardFragment)
} else {
Toast.makeText(context, R.string.wrong_inputs, Toast.LENGTH_LONG).show()
}
}
// [...] hidden code
}
I don't understand how this call, viewModel.insert(
can be asynchronous. Note AccountConfigurationRepository.update
is already a suspend
function. I tried by modifying like this without success (same error) :
- fun insert(account_configuration: AccountConfiguration) = viewModelScope.launch {
suspend fun insert(account_configuration: AccountConfiguration) = viewModelScope.launch {
lifecycleScope.launch { // coroutine on Main
viewModel.insert(
// ...
How can I make this database insert non blocking for UI ?
CodePudding user response:
Room DAO functions should always suspend so it can't block the main UI thread, you can refer this google recommended example: https://developer.android.com/codelabs/android-room-with-a-view-kotlin#5
CodePudding user response:
You have to make your DAO
methods suspend, so they don't block the UI thread
@Dao
interface AccountConfigurationDao {
@Query("SELECT * FROM accountconfiguration LIMIT 1")
fun get(): Flow<AccountConfiguration>
@Query("DELETE FROM accountconfiguration")
suspend fun clear() //make this suspend
@Insert
suspend fun insert(account_configuration: AccountConfiguration) //make this suspend
}
I've tried your github
code, and you've to uncomment implementation "androidx.room:room-runtime:$room_version"
.
I think there's a bug in Room 2.3.0
as it's giving Not sure how to handle query method's return type (java.lang.Object).
error, on adding suspend
keyword to DAO
. You should use Room 2.4.0-beta02
def room_version = "2.4.0-beta02"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"