title: Insert data with room from view : Cannot access database on the main thread
body: <p>I am a total beginner with kotlin and android development. I followed <a href="https://developer.android.com/codelabs/basic-android-kotlin-training-persisting-data-room" rel="nofollow noreferrer">Persist data with Room</a> tutorial and I have a popular problem to save my data:</p>
  <pre><code>Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
  </code></pre>
  <p>I understand why by reading other responses. But I don't understand how to deal with it.</p>
  <p>My entity and its DAO :</p>
  <pre class="lang-kotlin prettyprint-override"><code>@Entity
  data class AccountConfiguration(
      @PrimaryKey val server_address: String,
      @ColumnInfo(name = &quot;user_name&quot;) val user_name: String,
      @ColumnInfo(name = &quot;password&quot;) val password: String,  // FIXME : secure storage
      @ColumnInfo(name = &quot;notify_hungry&quot;) val notify_hungry: Boolean,
      @ColumnInfo(name = &quot;notify_thirsty&quot;) val notify_thirsty: Boolean,
      @ColumnInfo(name = &quot;notify_ap&quot;) val notify_ap: Boolean,
      @ColumnInfo(name = &quot;network_grab_each&quot;) val network_grab_each: Int,
  )
  
  @Dao
  interface AccountConfigurationDao {
      @Query(&quot;SELECT * FROM accountconfiguration LIMIT 1&quot;)
      fun get(): Flow&lt;AccountConfiguration&gt;
  
      @Query(&quot;DELETE FROM accountconfiguration&quot;)
      fun clear()
  
      @Insert
      fun insert(account_configuration: AccountConfiguration)
  }
  </code></pre>
  <p>Its view model :</p>
  <pre><code>
  class AccountConfigurationViewModel(private val repository: AccountConfigurationRepository) : ViewModel() {
  
      val accountConfiguration: LiveData&lt;AccountConfiguration&gt; = 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 &lt; 5 * 60) {
              return false
          }
          return true
      }
  
  }
  </code></pre>
  <p>The <a href="https://github.com/buxx/rolldash/blob/main/app/src/main/java/fr/bux/rollingdashboard/Database.kt" rel="nofollow noreferrer">database</a> and <a href="https://github.com/buxx/rolldash/blob/main/app/src/main/java/fr/bux/rollingdashboard/Application.kt" rel="nofollow noreferrer">Application</a>.</p>
  <p>Then, concerned part of the view (entire file <a href="https://github.com/buxx/rolldash/blob/main/app/src/main/java/fr/bux/rollingdashboard/AccountConfigurationFragment.kt" rel="nofollow noreferrer">here</a>) :</p>
  <pre class="lang-kotlin prettyprint-override"><code>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
  }
  </code></pre>
  <p>I don't understand how this call, <code>viewModel.insert(</code> can be asynchronous. Note <code>AccountConfigurationRepository.update</code> is already a <code>suspend</code> function. I tried by modifying like this without success (same error) :</p>
  <pre><code>-    fun insert(account_configuration: AccountConfiguration) = viewModelScope.launch {
  +    suspend fun insert(account_configuration: AccountConfiguration) = viewModelScope.launch {
  </code></pre>
  <pre><code>            lifecycleScope.launch { // coroutine on Main
                  viewModel.insert(
                      // ...
  </code></pre>
  <p>How can I make this database insert non blocking for UI ?</p>