I am creating a pixel art editor app and I already have one Room database which stores the users' creations and another Room database which I want to add which will store some custom color palettes the user wants to add to the app.
To do this I added the following database:
@Database(entities = [ColorPalette::class], version = 3)
abstract class ColorPalettesDatabase: RoomDatabase() {
abstract fun colorPalettesDao(): ColorPalettesDao
companion object {
private var instance: ColorPalettesDatabase? = null
fun getDatabase(context: Context): ColorPalettesDatabase {
if (instance == null) {
synchronized(ColorPalettesDatabase::class) {
if (instance == null) instance = Room.databaseBuilder(context.applicationContext, ColorPalettesDatabase::class.java, AppData.colorPalettesDBFileName).allowMainThreadQueries().build()
}
}
return instance!!
}
}
}
And the DAO:
@Dao
interface ColorPalettesDao {
@Insert
suspend fun insertColorPalette(colorPalette: ColorPalette)
@Query("SELECT * FROM ColorPalette ")
fun getAllColorPalettes(): LiveData<List<ColorPalette>>
@Query("DELETE FROM ColorPalette WHERE objId=:colorPaletteId")
fun deleteColorPalette(colorPaletteId: Int)
}
I added a variable in AppData
and initialized it in the MainActivity's onCreate
method:
class AppData {
companion object {
var pixelArtDBFileName = "pixel_art_db"
lateinit var pixelArtDB: PixelArtDatabase
var colorPalettesDBFileName = "color_palettes_db"
lateinit var colorPalettesDB: ColorPalettesDatabase
}
}
AppData.colorPalettesDB = ColorPalettesDatabase.getDatabase(this)
And finally, I use get the ColorPalette
data from the database in the ColorPalettesFragment.kt
file:
class ColorPalettesFragment(private val lifecycleOwner: LifecycleOwner) : Fragment(), ColorPalettesListener {
private var _binding: FragmentColorPalettesBinding? = null
private val binding get() = _binding!!
private lateinit var caller: ColorPalettesFragmentListener
private fun setUpRecyclerView() {
binding.apply {
fragmentColorPalettesRecyclerView.layoutManager = LinearLayoutManager([email protected]).apply {
orientation = LinearLayoutManager.HORIZONTAL
}
AppData.colorPalettesDB.colorPalettesDao().getAllColorPalettes().observe(lifecycleOwner) {
fragmentColorPalettesRecyclerView.adapter = ColorPalettesAdapter(it, this@ColorPalettesFragment)
}
}
}
companion object {
fun newInstance(lifecycleOwner: LifecycleOwner) = ColorPalettesFragment(lifecycleOwner)
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is ColorPalettesFragmentListener) caller = context
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentColorPalettesBinding.inflate(inflater, container, false)
setUpRecyclerView()
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onColorPaletteTapped(selectedColorPalette: ColorPalette) {
caller.onColorPaletteTapped(selectedColorPalette)
}
}
So far I'm testing it when the database is empty, what I expect is whenever the user taps the following button they will see a blank RecyclerView
:
Unfortunately, I can't even get to the screen as when I run the app and try to navigate to the Canvas I get the following exception:
2021-12-22 08:55:57.251 24474-24554/com.realtomjoney.pyxlmoose E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_1
Process: com.realtomjoney.pyxlmoose, PID: 24474
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
Caused by: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:154)
at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:135)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:201)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:427)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:151)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:112)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483)
at androidx.room.RoomDatabase.query(RoomDatabase.java:526)
at androidx.room.util.DBUtil.query(DBUtil.java:86)
at com.realtomjoney.pyxlmoose.dao.ColorPalettesDao_Impl$4.call(ColorPalettesDao_Impl.java:108)
at com.realtomjoney.pyxlmoose.dao.ColorPalettesDao_Impl$4.call(ColorPalettesDao_Impl.java:105)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
I've tried to look at other solutions for a fix but nothing seems to help.
This is coming from the ColorPalettesDatabase
as when I remove all of its usages in code the exception goes away.
Any help would be appreciated so I can add color palette functionality to my app.
Updating the version unfortunately doesn't help and so doesn't uninstalling the app.
Funny comment: I was thinking to bounty this in the future but unfortunately I can't cause I wasted 150 reputation on a question which got no reply lol
CodePudding user response:
It sounds like you have an existing version of the database and have changed the schema (i.e. changed the ColorPalette
class).
Room has detected this change and is therefore suggesting that you increase the version number. However, you don't appear to have any Migrations therefore changing the version number would then fail.
IF you have no data (sounds like the case as per So far I'm testing it when the database is empty, what I expect is whenever the user taps the following button they will see a blank RecyclerView:). Coding .fallbackToDestructiveMigration
might overcome this issue as in the absence of a Migration it will drop the tables and then create the tables.
However, the above doesn't answer and so doesn't uninstalling the app.. So at a guess you then have an ongoing issue.
I would suggest first adding .fallbackToDestructiveMigration
and then rerunning, if this fails then edit you question to include the failure.
Next, assuming a failure, try uninstalling and rerunning and then, assuming it fails, editing your question with the failure under that scenario.
Demo of problem as per question
ColorPalette class - see comments regarding runs
@Entity
data class ColorPalette(
/* original V3 code */
@PrimaryKey
@ColumnInfo(name = "objId")
val objId: Long? = null,
val description: String,
val color: Long
/* additional code */
,
val extra: String
)
ColorPaletteDao (same bar no LiveData)
@Dao
interface ColorPalettesDao {
@Insert
fun insertColorPalette(colorPalette: ColorPalette)
@Query("SELECT * FROM ColorPalette ")
fun getAllColorPalettes(): /*LiveData<*/List<ColorPalette>/*>*/ /* for testing without LiveData */
@Query("DELETE FROM ColorPalette WHERE objId=:colorPaletteId")
fun deleteColorPalette(colorPaletteId: Int)
}
ColorPalleteDatabase (identical but see later)
MainActivity to demonstrate
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val db = ColorPalettesDatabase.getDatabase(this)
val dao = db.colorPalettesDao()
dao.getAllColorPalettes()
}
}
Run 1 - only original code for ColorPalette i.e. no extra column.
- Runs fine database created (as per AppInspection):-
Run 2 - introduce the extra column, do nothing else, and run
- ooops!!!!
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so70444736kotlinroomincreaseversion/a.a.so70444736kotlinroomincreaseversion.MainActivity}: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
Run 3 - Increase version number from 3 to 4 and run
- ooops!!!!
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so70444736kotlinroomincreaseversion/a.a.so70444736kotlinroomincreaseversion.MainActivity}: java.lang.IllegalStateException: A migration from 3 to 4 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
Run 4 add .fallbackToDestructiveMigration()
to ColorPaletteDatabase and run
- Runs OK, database :-
RESET EVERYTHING BACK to initial state (including uninstalling the App) and run Run 1 and Run 2
- same issue as per Run 2 as expected.
Uninstall App but without any changes (so extra column and version 3)
- runs fine and database has the extra column
Hence
Why it is suggested that there are other issues, as both suggested initial fixes :-
- increase version along with
.fallbackToDestructiveMigration
, and - uninstall App and rerun
both work to correct the reported issue.