I'm encountering a Fatal Exception: java.lang.IllegalStateException in my Android application when accessing the Room Database DAO after the database connection pool has been closed.
Inside my database i have suspend function to clean database.
suspend fun formatAndCleanDatabase(context: Context){
clearAllData()
close()
context.deleteDatabase("app_database")
clearDatabaseInstance()
}
When i clearing tables and closing database the flow try get data after closed which i want to how prevent this.
@Dao
interface UserProfileDao {
@Query("SELECT * FROM user_profile WHERE user_id = :userId")
fun getProfileFlow(userId: String): Flow<UserProfile>
}
The error occurs after cleaning database. And earlier flow already invoked by view model before cleaning the database is try fetch data from the database using Flow in a DAO method after database is closed and invalidated. Here’s the stack trace:
Fatal Exception: java.lang.IllegalStateException
Cannot perform this operation because the connection pool has been closed.
android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked (SQLiteConnectionPool.java:1078)
androidx.room.driver.SupportSQLiteStatement$SupportAndroidSQLiteStatement.step (SupportSQLiteStatement.android.kt:183)
com.app.database.daos.profile.UserProfileDao_Impl.getProfileFlow$lambda$2 (UserProfileDao_Impl.kt:148)
Deleting the entire database while it is currently in use is an unusual thing to do, so I doubt there is an official recommendation how to handle that. But you can always catch the exception and process it accordingly.
Assuming your view model transforms the database flow in a StateFlow of UiState, you can do something like this:
sealed interface UiState {
data object Loading : UiState
data class Success(val profile: UserProfile) : UiState
data object NoDatabase : UiState
}
class MyViewModel(dao: UserProfileDao, userId: String) : ViewModel() {
val uiState: StateFlow<UiState> = dao.getProfileFlow(userId)
.mapLatest(UiState::Success)
.catch<UiState> {
if (it !is IllegalStateException) throw it
emit(UiState.NoDatabase)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5.seconds),
initialValue = UiState.Loading,
)
}
Your UI can then react accordingly and display what needs to be displayed when the database was deleted.
You would need to do something similar to all other dao functions that also return a flow.