School项目,对于Android开发我还是很陌生。
问题
我在保存人员片段中有一个带有onClick监听器的按钮,该片段会将人员数据保存到数据库中。一切正常,除了出于某种原因第一次单击带有之外,它不会向我返回插入的行ID ,但第二次单击后仍会返回。
在继续下一个片段之前,我确实需要此ID。
不确定这是否重要,但是每当我返回(重新加载)此保存人员片段时,其行为始终相同,即始终单击均无法捕获插入的行ID。
输入数据:

first name = John
last name = Smith
仅出于演示目的,如果我尝试使用此按钮3x插入人员数据(返回的插入ID在日志中),我将在数据库中获得所有名为John Smith的3行,但最先插入的行ID为未捕获(默认初始化值为 0 ),请参见以下日志:
日志
2020-10-19 12:49:20.320 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 0
2020-10-19 12:49:40.153 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 5
2020-10-19 12:49:40.928 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 6
原始编辑帖子
正如评论中所建议的那样,我正在尝试使用LiveData和observer的方式,但是我仍然有些卡住。
设置
以下是当前设置。
实体
@Entity(tableName = "person")
data class Person(

    @PrimaryKey(autoGenerate = true)
    val id: Int,
DAO
@Dao
interface PersonDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun addPerson(person: Person): Long
存储库
class PersonRepository(private val personDao: PersonDao) {

    val readAllPersonData: LiveData<List<Person>> = personDao.readAllPersonData()

    suspend fun addPerson(person: Person): Long {
        return personDao.addPerson(person)
    }
ViewModel
我不确定我是否在这里做所有事情。我在这里分步分解并创建了单独的变量insertedPersonILiveDatainsertedPersonId
如何将返回的行ID传递给insertedPersonILiveData
class PersonViewModel(application: Application) : AndroidViewModel(application) {
        var insertedPersonILiveData: LiveData<Long> = MutableLiveData<Long>()
        var insertedPersonId: Long = 0L

        val readAllPersonData: LiveData<List<Person>>
        private val repository: PersonRepository

        init {
            val personDao = ContactDatabase.getDatabase(application).personDao()
            repository = PersonRepository(personDao)
            readAllPersonData = repository.readAllPersonData
        }

        suspend fun addPerson(person: Person) = viewModelScope.launch {
            insertedPersonId = repository.addPerson(person)
        // ****************************************************************
        // insertedPersonILiveData = insertedPersonId (what to do here) ???
        // ****************************************************************
        }
保存人片段
这是我通过modelView调用addPerson的方式。
val person = Person(0, firstName, lastName)

lifecycleScope.launch {
    personViewModel.addPerson(person)
}
Log.d("TEST_ADD_PERSON_ID","insertedPersonId: ${personViewModel.insertedPersonId}")
这就是我做观察者的方式(不确定它是否正确)。
val returnedIdListener: LiveData<Long> = personViewModel.insertedPersonILiveData
returnedIdListener.observe(viewLifecycleOwner, Observer<Long> { id: Long ->
    goToAddContactFragment(id)
})


private fun goToAddContactFragment(id: Long) {
    Log.d("TEST_ADD_PERSON_ID", "id: " + id)
}
创建数据库
@Database(
    entities = [Person::class, Contact::class, ContactType::class],
    views = [ContactDetails::class],
    version = 1,
    exportSchema = false
)
abstract class ContactDatabase : RoomDatabase() {

    abstract fun personDao(): PersonDao
    abstract fun contactTypeDao(): ContactTypeDao
    abstract fun contactDao(): ContactDao
    abstract fun contactDetailsDao(): ContactDetailsDao

    companion object {

        // For Singleton instantiation
        @Volatile
        private var instance: ContactDatabase? = null

        fun getDatabase(context: Context): ContactDatabase {
            return instance ?: synchronized(this) {
                instance ?: buildDatabase(context).also { instance = it }
            }
        }

        private fun buildDatabase(context: Context): ContactDatabase {
            return Room.databaseBuilder(context, ContactDatabase::class.java, "contacts_database")
                .addCallback(
                    object : RoomDatabase.Callback() {
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            super.onCreate(db)
                            val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
                            WorkManager.getInstance(context).enqueue(request)
                        }
                    }
                )
                .build()
        }
    }
}

最佳答案

您正在启动协程以运行addPerson,然后立即在 View 模型中使用Log的当前值调用insertedPersonId。协程将运行,插入人员,并使用插入的行的ID更新VM,但这将在Log运行之后很长时间发生。可能所有结果实际上都是最后插入的记录的ID。
我也是很多新手,但是仅根据您现在的情况,我认为您只需要添加

insertedPersonILiveData.value = insertedPersonId
在您的addPerson函数中。这样,您将使用新值更新LiveData,它将被推送到任何有效的观察者。您已经编写了一些代码来观察LiveData实例,因此在设置它时应该可以获取更新。

编辑您的问题是insertedPersonILiveData是不可变的LiveData类型,因此您无法在其上设置值-它是只读的。您正在创建MutableLiveData对象,但是将其作为LiveData类型公开。
推荐的模式是将可变类型创建为内部对象,将其引用公开为不可变类型,并创建一个setter方法,该方法通过可变引用(可在内部访问)更改值。
class myViewModel : ViewModel() {
    // mutable version is private, all updates go through the setter function
    // (the _ prefix is a convention for "private versions" of data fields)
    private val _lastInsertedPersonId = MutableLiveData<Long>()

    // we're making the instance accessible (for observing etc), but as
    // the immutable LiveData supertype that doesn't allow setting values
    val lastInsertedPersonId: LiveData<Long> = _lastInsertedPersonId

    // setting the value on the MutableLiveData instance
    // is done through this public function
    fun setLastInsertedPersonId(id: Long) {
        _lastInsertedPersonId.value = id
    }
}
然后您的观察者只需调用lastInsertedPersonId.observe,您就无需复制LiveData并观察它(就像您正在使用returnedIdListener一样。
这就是那里的基本模式-内部MutableLiveData,公开为不可变LiveData val,并使用setter方法更新值。 View 模型之外的所有内容要么观察可见的LiveData,要么调用setter方法进行更新。希望有道理!一旦掌握了基本情况,事情就没那么复杂了

关于kotlin - Android ROOM,insert不会在第一次插入时返回ID,但是会在第二次插入之后返回ID,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/64426165/

10-12 03:58