I use rxjava to read from the room database on the Schedulers.io thread. I've noticed that when my app is killed and reopened it always crashes with
Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
I use MVVM architecture with Dagger and instantiate view model with lazy
private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(StudentsListViewModel::class.java) }
All my requests to database look like this:
compositeDisposable.add(viewModel.students
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
<I do things here>
}
.doOnError { it.printStackTrace() }
.subscribe()
)
I kill disposable onDestroy with
compositeDisposable.apply {
clear()
dispose()
}
So, why is it crushing on recreate and what is it trying to do on the main thread?
My ModelView class:
class StudentsListViewModel@Inject
constructor(private val studioRepository: StudioRepository): ViewModel() {
val students: Flowable<List<Student>> = studioRepository.students
fun getStudentById(studentId: Int): Maybe<Student> {
return studioRepository.getStudentById(studentId)
}
fun insertStudent(student: Student): Unit = studioRepository.insertStudent(student)
fun insertStudentFlat(student: Student) = studioRepository.insertStudentFlat(student)
fun updateStudent(student: Student) {
studioRepository.updateStudent(student)
}
fun deleteAllStudents() {
studioRepository.deleteAllStudents()
}
fun getStudentsNames(): Flowable<List<String>> = studioRepository.studentNames
val studentsCount: Flowable<Int> = studioRepository.studentsCount
fun getTicketById(ticketId: Int): Maybe<Ticket> {
return studioRepository.getTicketById(ticketId)
}
fun getTicketByIdFlat(ticketId: Int): Ticket {
return studioRepository.getTicketByIdFlat(ticketId)
}
fun updateTicket(ticket: Ticket) = studioRepository.updateTicket(ticket)
val activeTicketsStudentIds: Flowable<List<Int>> = studioRepository.activeTicketsStudentIds
fun getClassesByIdFlat(values: List<Int>): List<YClass> = studioRepository.getYclassesByIdFlat(values)
private fun getStudent(studentId: Int): Maybe<Student> = studioRepository.getStudentById(studentId)
private fun getAllActiveTicketsForAStudent(studentId: Int): Maybe<List<Ticket>> = studioRepository.getAllActiveTicketsForAStudent(studentId)
fun archiveStudentByIdAndDeactivateHisTickets(id: Int) {
getStudent(id)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.doOnSuccess {
it.isArchived = true
studioRepository.updateStudent(it)
getAllActiveTicketsForAStudent(id)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.doOnSuccess { list ->
list.forEach {
it.actual = false
updateTicket(it)
}
}
?.subscribe()
}
.doOnError {
it.printStackTrace()
}
.subscribe()
}
}
08-03 22:17:45.601 7776-7776/com.labfoodandfriends.nikitagudkovs.jlogOpenClasses E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.labfoodandfriends.nikitagudkovs.jlogOpenClasses, PID: 7776
java.lang.RuntimeException: **Unable to start activity** ComponentInfo{com.labfoodandfriends.nikitagudkovs.jlogOpenClasses/com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsActivity}: java.lang.RuntimeException: 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.performLaunchActivity(ActivityThread.java:2778)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.RuntimeException: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at com.labfoodandfriends.nikitagudkovs.jlog.factory.AppViewModelFactory.create(AppViewModelFactory.java:55)
at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134)
at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsActivity$viewModel$2.invoke(StudentsActivity.kt:83)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsActivity$viewModel$2.invoke(StudentsActivity.kt:69)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsActivity.getViewModel(Unknown Source:7)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsActivity.observeStudents(StudentsActivity.kt:136)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsActivity.onCreate(StudentsActivity.kt:103)
at android.app.Activity.performCreate(Activity.java:7009)
at android.app.Activity.performCreate(Activity.java:7000)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: 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.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:204)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:232)
at com.labfoodandfriends.nikitagudkovs.jlog.database.dao.StudentDAO_Impl.getStudents(StudentDAO_Impl.java:272)
at com.labfoodandfriends.nikitagudkovs.jlog.repository.StudioRepositoryImpl.<init>(StudioRepositoryImpl.kt:50)
at com.labfoodandfriends.nikitagudkovs.jlog.di.AppModule.provideStudioRepository(AppModule.kt:26)
at com.labfoodandfriends.nikitagudkovs.jlog.di.AppModule_ProvideStudioRepositoryFactory.get(AppModule_ProvideStudioRepositoryFactory.java:24)
at com.labfoodandfriends.nikitagudkovs.jlog.di.AppModule_ProvideStudioRepositoryFactory.get(AppModule_ProvideStudioRepositoryFactory.java:10)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsListViewModel_Factory.get(StudentsListViewModel_Factory.java:17)
at com.labfoodandfriends.nikitagudkovs.jlog.activity.students.StudentsListViewModel_Factory.get(StudentsListViewModel_Factory.java:8)
at com.labfoodandfriends.nikitagudkovs.jlog.factory.AppViewModelFactory.create(AppViewModelFactory.java:53)
> = studioRepository.students` - as per stacktrace you are getting the students and this is evaluated immediately on the calling thread that instantiates the class.
– Mark Aug 05 '18 at 12:43> = studioRepository.students with subscribeOn(Schedulers.io()) from the View
– Tuesday Four AM Aug 05 '18 at 21:47> = Flowable.defer { studioRepository.students }`
– Mark Aug 05 '18 at 22:10