School项目,对于Android开发我还是很陌生。
问题
我在保存人员片段中有一个带有onClick监听器的按钮,该片段会将人员数据保存到数据库中。一切正常,除了出于某种原因第一次单击带有之外,它不会向我返回插入的行ID ,但第二次单击后仍会返回。
在继续下一个片段之前,我确实需要此ID。
不确定这是否重要,但是每当我返回(重新加载)此保存人员片段时,其行为始终相同,即始终单击均无法捕获插入的行ID。
输入数据:
仅出于演示目的,如果我尝试使用此按钮3x插入人员数据(返回的插入ID在日志中),我将在数据库中获得所有名为John Smith的3行,但最先插入的行ID为未捕获(默认初始化值为 0 ),请参见以下日志:first name = John
last name = Smith
日志
原始编辑帖子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的方式,但是我仍然有些卡住。
设置
以下是当前设置。
实体
DAO @Entity(tableName = "person")
data class Person(
@PrimaryKey(autoGenerate = true)
val id: Int,
存储库@Dao
interface PersonDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addPerson(person: Person): Long
ViewModel class PersonRepository(private val personDao: PersonDao) {
val readAllPersonData: LiveData<List<Person>> = personDao.readAllPersonData()
suspend fun addPerson(person: Person): Long {
return personDao.addPerson(person)
}
我不确定我是否在这里做所有事情。我在这里分步分解并创建了单独的变量insertedPersonILiveData
和insertedPersonId
。
如何将返回的行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/