学校项目,我对Android开发还很陌生。
问题
我在保存人员片段中有一个带有onClick侦听器的按钮,该按钮会将人员数据保存到数据库中。一切正常,除了由于某些原因第一次单击而不会返回插入的行ID ,但是第二次单击会返回我。
在继续下一个片段之前,我真的需要这个ID。
不确定这是否重要,但是每当我返回(重新加载)此保存人员片段时,其行为始终相同,即始终单击均无法捕获插入的行ID。>
输入数据:
first name = John
last name = Smith
仅出于演示目的,如果我尝试使用此按钮3x插入人员数据(返回的插入ID在日志中),我将在数据库中获得所有3行,名称为John Smith,但第一个插入未捕获行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
我不确定我在这里做的事情是否正确。我在这里分步进行了分解,并创建了单独的变量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()
}
}
}
答案 0 :(得分:1)
您正在启动协程以运行addPerson
,然后立即在视图模型中使用当前值为Log
调用insertedPersonId
。协程将运行,插入人员,并使用插入行的ID更新VM,但这将在您的Log
运行很长时间之后发生。可能您所有的结果实际上都是最后插入的记录的ID。
我也是很多新手,但是根据您现在的情况,我认为您只需要添加
insertedPersonILiveData.value = insertedPersonId
在您的addPerson
函数中。这样,您将使用新值更新该LiveData
,该值将被推送到任何有效的观察者。您已经编写了一些用于观察LiveData
实例的代码,因此在设置该实例时应该可以获取更新。
编辑,您的问题是insertedPersonILiveData
是不可变的LiveData
类型,因此您无法为其设置值-它是只读的。您正在创建一个MutableLiveData
对象,但是将其作为LiveData
类型公开。
推荐的模式是创建一个 mutable 作为内部对象,将其引用公开为 immutable 类型,并创建一个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方法更新值。视图模型之外的所有内容都会观察到可见的LiveData
,或调用setter方法进行更新。希望有道理!一旦掌握了基本发生的情况,事情就没那么复杂了