Fixed
Status Update
Comments
yb...@google.com <yb...@google.com>
yb...@google.com <yb...@google.com> #2
The problem is reproducible using support lib 26.1.0, this is the updated activity:
class LiveDataTestActivity : AppCompatActivity() {
private val myLiveData = MutableLiveData<Int>().apply { value = 0 }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val textView = TextView(this).apply {
text = "AAAA"
setOnClickListener {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "This is my text to send.")
type = "text/plain"
}
startActivityForResult(sendIntent, 123)
}
}
setContentView(textView)
myLiveData.observe(this, Observer<Int> {
textView.text = it.toString()
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
myLiveData.value = (myLiveData.value ?: 0) + 1
println("Value ${myLiveData.value}")
}
override fun onResume() {
super.onResume()
println("resume ${lifecycle.currentState}")
}
}
class LiveDataTestActivity : AppCompatActivity() {
private val myLiveData = MutableLiveData<Int>().apply { value = 0 }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val textView = TextView(this).apply {
text = "AAAA"
setOnClickListener {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "This is my text to send.")
type = "text/plain"
}
startActivityForResult(sendIntent, 123)
}
}
setContentView(textView)
myLiveData.observe(this, Observer<Int> {
textView.text = it.toString()
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
myLiveData.value = (myLiveData.value ?: 0) + 1
println("Value ${myLiveData.value}")
}
override fun onResume() {
super.onResume()
println("resume ${lifecycle.currentState}")
}
}
yb...@google.com <yb...@google.com> #3
thanks for the reports.
After further investigation:
good news, this is not a very serious bug, it recovers right after onResume. (also the bug does not happen in latest versions of the OS)
bad news, properly fixing it requires changes in the support library so it may not catch 1.0.0 release.
details:
Lifecycles moves state to CREATED when onSavedInstanceState is called. This is done because FragmentManager disables fragment transactions when onSavedInstanceState is called. Lifecycles API guaratees that you can run a fragment transaction inside an observer. This behavior is usually fine because onSavedInstanceState is always followed by onStop.
Turns out, this is not the case for API21..
things to do:
* make sure we have tests that fragments are handled properly and observers don't receive an unexpected STARTED event.
* in the next support lib version, make sure the state is set to at least STARTED when FragmentController#noteStateNotSaved is called.
After further investigation:
good news, this is not a very serious bug, it recovers right after onResume. (also the bug does not happen in latest versions of the OS)
bad news, properly fixing it requires changes in the support library so it may not catch 1.0.0 release.
details:
Lifecycles moves state to CREATED when onSavedInstanceState is called. This is done because FragmentManager disables fragment transactions when onSavedInstanceState is called. Lifecycles API guaratees that you can run a fragment transaction inside an observer. This behavior is usually fine because onSavedInstanceState is always followed by onStop.
Turns out, this is not the case for API21..
things to do:
* make sure we have tests that fragments are handled properly and observers don't receive an unexpected STARTED event.
* in the next support lib version, make sure the state is set to at least STARTED when FragmentController#noteStateNotSaved is called.
Description
Version used: 1.0.3
Devices/Android versions reproduced on: Emulator and Pixel XL
When I try to go from version 3 to 2 I get the following exception:
01-18 09:33:21.376 8183-8248/
Process:
java.lang.IllegalStateException: A migration from 3 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
at android.arch.persistence.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:82)
at android.arch.persistence.room.RoomOpenHelper.onDowngrade(RoomOpenHelper.java:94)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onDowngrade(FrameworkSQLiteOpenHelper.java:128)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:297)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:194)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:93)
at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:193)
Here is my database creation code:
fun createDatabase(application: Application): UserDatabase {
return Room.databaseBuilder(application, UserDatabase::class.java, getDatabaseName(userManager.userId))
.addMigrations(AddStep1_2, DropStep2_1, AddDatum2_3, DropDatum3_2)
.addCallback(UserDatabase.Callback)
.build()
}
Here is my version 2 database class:
@Database(version = 2, entities = [
/* 1 */ Tag::class, Impression::class, Goal::class,
/* 2 */ GoalQuestion::class, Question::class, Step::class, StepCompletion::class//,
/* 3 */ //Datum::class
])
@TypeConverters(ConvertDateTime::class, ConvertStepFrequency::class, ConvertTagId::class, ConvertUUID::class)
abstract class UserDatabase : RoomDatabase() {
...
}
Here are my migrations:
object AddStep1_2 : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
...
}
}
object DropStep2_1 : Migration(2, 1) {
override fun migrate(db: SupportSQLiteDatabase) {
...
}
}
object AddDatum2_3 : Migration(2, 3) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE TABLE IF NOT EXISTS `Datum` (`key` TEXT NOT NULL PRIMARY KEY, `value` TEXT)")
}
}
object DropDatum3_2 : Migration(3, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS `Datum`")
}
}
-------
Looking at the RoomDatabase source code, lines 536-544, the 'if' conditions are the source of the problem, which will only find forward migrations, even though you have an 'upgrade' variable just above which is false for reverse migrations:
for (int i = firstIndex; i != lastIndex; i += searchDirection) {
int targetVersion = targetNodes.keyAt(i);
if (targetVersion <= end && targetVersion > start) {
result.add(targetNodes.valueAt(i));
start = targetVersion;
found = true;
break;
}
}