i tried to call ViewModel to delete table when activity Destroy but it not work, when i call ViewModel when activity stop i get a error.
DAO interface
@Dao
interface NoteDAO {
@Query("select * from note_table")
fun getAllNote() : LiveData<List<Note>>
@Query("DELETE FROM note_table")
fun deleteAllData()
}
Repository
class NoteRepository(app:Application) {
private val noteDAO : NoteDAO
init {
val noteDatabase: NoteDatabase = NoteDatabase.getInstance(app)
noteDAO = noteDatabase.getNoteDao()
}
fun getAllNote():LiveData<List<Note>> = noteDAO.getAllNote()
fun deleteAllData() = noteDAO.deleteAllData()
}
Viewmodel
class NoteViewModel(app: Application): ViewModel() {
private val noteRepository: NoteRepository = NoteRepository(app)
fun deleteAllData() = noteRepository.deleteAllData()
fun getAllNote() : LiveData<List<Note>> = noteRepository.getAllNote()
class NoteViewModelFactory(private val application: Application) : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass.isAssignableFrom(NoteViewModel::class.java)){
return NoteViewModel(application) as T
}
throw IllegalArgumentException("Unable construct viewmodel")
}
}
}
when i call in onStop method
override fun onStop() {
super.onStop()
noteViewModel.deleteAllData()
}
i get the error
java.lang.RuntimeException: Unable to stop activity {com.example.database/com.example.database.MainActivity}: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:4624)
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4594)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4669)
at android.app.servertransaction.StopActivityItem.execute(StopActivityItem.java:41)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
please help me and have a nice day,everyone!
CodePudding user response:
You are running a database query on Main Thread
which could potentially block UI. You should use coroutines to execute your queries on the background thread.
Make your deleteAllData
method suspend
@Query("DELETE FROM note_table")
suspend fun deleteAllData()
Also, make deleteAllData
method suspend
in NoteRespository
suspend fun deleteAllData() = noteDAO.deleteAllData()
In your NoteViewModel
class, call this method in viewModelScope
fun deleteAllData() = viewModelScope.launch {
noteRepository.deleteAllData()
}
CodePudding user response:
if you want to perform database operation on main thread then add this line while building database object
.allowMainThreadQueries()
Full Sample
Room.databaseBuilder(appContext,LocalDatabase::class.java,"app.db")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
But It's not good practice to perform a database operation on the main thread. You must use coroutines to perform database operation
CodePudding user response:
you can use couroutine
override fun onStop() {
super.onStop()
GlobalScope.launch{
noteViewModel.deleteAllData()
}
}
or you can write it in your view model like it
fun deleteAllData() = viewModelScope.launch {
noteRepository.deleteAllData()
}
CodePudding user response:
I would do it in the onStop lifecycle function, calling the viewmodel, and in the viewmodel calling a coroutine executed in a background thread.
Activity/Fragment:
override fun onStop() {
viewModel.wipeData()
}
ViewModel:
class ViewModel @Inject constructor (wipeDataUseCase: WipeDataUseCase){
fun wipeData() {
viewModelScope.launch(IO) {
when (wipeDataUseCase()){
true -> {
//do something
}
false -> {
// do something
}
}
}
}
}
Use Case:
class WipeDataUseCase @Inject constructor (roomDataBase: RoomDataBase){
suspend operator fun invoke() = roomDataBase.dao().wipeData()
}
I would've use the coroutine mentioned above, launching the process in the background calling its respective Use Case.
As it is meant to be on the moment that the app is over there is no much thing to play with the callback of the wiping data operation.