从SharedFlow中尝试收集数据时始终获取null。

huangapple go评论68阅读模式
英文:

Getting null whenever trying to collect data from SharedFlow

问题

以下是您要翻译的代码部分:

DAO

@Query("SELECT * FROM employees WHERE id = :id")
fun getEmployeeById(id: Int?): Flow<EmployeeModel>

RepositoryInterface

fun getEmployeeById(id: Int?): Flow<EmployeeModel>

RepositoryImplementation

override fun getEmployeeById(id: Int?): Flow<EmployeeModel> {
    return employeeDAO.getEmployeeById(id)
}

ViewModel

var employeeById: SharedFlow<DatabaseState<EmployeeModel>> = repository.get().getEmployeeById(employeeId.value?.toInt())
    .map {
        println("onCreateView in VM. ID ${employeeId.value} | data: $it")
        DatabaseState.Success(it)
    }
    .catch { DatabaseState.Error(it.message) }
    .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)

Fragment

viewLifecycleOwner.lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        mViewModel.employeeById.collect{ employee ->
            when (employee){
                is DatabaseState.Success -> {
                    Log.i(TAG, "onCreateView APDEJCIK: ${mViewModel.employeeId.value} | ${employee.data}")
                }
                is DatabaseState.Error -> {
                    Log.i(TAG, "onCreateView: Failed to retrieve data about employee in UpdateFragmentEmployee fragment")
                }
            }
        }
    }
}

Model class

@Entity(tableName = "employees")
data class EmployeeModel(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "surname")
    val surname: String,
    @ColumnInfo(name = "age")
    val age: Int,
    @ColumnInfo(name = "workplace")
    val workplace: String,
    @ColumnInfo(name = "salary")
    val salary: Double
)
英文:

I have a SharedFlow in ViewModel, where I make a call to repository, to retrieve from the database a single record by some id. This is will be set whenever user will click on some record in RecyclerView. The problem is I constantly getting null, but if I hardcode id in repository parameter, then everything works fine.

DAO

@Query(&quot;SELECT * FROM employees WHERE id = :id&quot;)
fun getEmployeeById(id: Int?): Flow&lt;EmployeeModel&gt;

RepositoryInterface

fun getEmployeeById(id: Int?): Flow&lt;EmployeeModel&gt;

RepositoryImplementation

    override fun getEmployeeById(id: Int?): Flow&lt;EmployeeModel&gt; {
    return employeeDAO.getEmployeeById(id)
}

ViewModel

var employeeById: SharedFlow&lt;DatabaseState&lt;EmployeeModel&gt;&gt; = repository.get().getEmployeeById(employeeId.value?.toInt())
    .map {
        println(&quot;onCreateView in VM. ID ${employeeId.value}  |  data: $it&quot;)
        DatabaseState.Success(it) }
    .catch { DatabaseState.Error(it.message) }
    .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)

Fragment

viewLifecycleOwner.lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
           mViewModel.employeeById.collect{ employee -&gt;
               when (employee){
                   is DatabaseState.Success -&gt; {
                       Log.i(TAG, &quot;onCreateView APDEJCIK: ${mViewModel.employeeId.value} | ${employee.data}&quot;)
                   }
                   is DatabaseState.Error -&gt; {
                       Log.i(TAG, &quot;onCreateView: Failed to retrieve data about employee in UpdateFragmentEmployee fragment&quot;)}
               }
           }
        }
    }

As you can see, I'm loging the ID from the ViewModel a couple of times, and it every times has correct id to the position I've clicked, so the ID should be ok.

Edit:
Model class

    @Entity(tableName = &quot;employees&quot;)
data class EmployeeModel(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    @ColumnInfo(name = &quot;name&quot;)
    val name: String,
    @ColumnInfo(name = &quot;surname&quot;)
    val surname: String,
    @ColumnInfo(name = &quot;age&quot;)
    val age: Int,
    @ColumnInfo(name = &quot;workplace&quot;)
    val workplace: String,
    @ColumnInfo(name = &quot;salary&quot;)
    val salary: Double
)

答案1

得分: 0

以下是翻译好的部分:

"I think these code below have a problem"(我认为以下的代码存在问题)

"Because of this declaration, your employeeById will be created when your view model is created, so employeeId.value is still null."(由于这个声明,当您的视图模型创建时,employeeById 将被创建,因此 employeeId.value 仍然为空。)

"Then, because of SharingStarted.WhileSubscribed(), the map function will only get called when your flow has a subscriber on your repeatOnLifecycle(Lifecycle.State.STARTED). At this time, your employeeId.value is set the correct value. This is why you get a very weird log."(然后,由于 SharingStarted.WhileSubscribed(),map 函数只在您的流具有订阅者时才会被调用,例如在 repeatOnLifecycle(Lifecycle.State.STARTED) 上。在这个时候,employeeId.value 将设置为正确的值。这就是为什么您会得到一个非常奇怪的日志。)

"To fix your issue, I think some things need to be changed."(为了解决您的问题,我认为需要做一些更改。)

"Your DAO"(您的数据访问对象)

"Your viewModel. I assume that you have a state flow for your employee. You should use flatMap to update your employeeById value whenever your employee changes."(您的视图模型。我假设您对员工有一个状态流。您应该使用 flatMap 来在员工更改时更新 employeeById 的值。)

"Last thing, if you use employeeById to display data. Consider using StateFlow instead of SharedFlow"(最后一件事,如果您使用 employeeById 来显示数据,请考虑使用 StateFlow 而不是 SharedFlow。)

英文:

I think these code below have a problem

var employeeById: SharedFlow&lt;DatabaseState&lt;EmployeeModel&gt;&gt; = repository.get().getEmployeeById(employeeId.value?.toInt())
    .map {
        println(&quot;onCreateView in VM. ID ${employeeId.value}  |  data: $it&quot;)
        DatabaseState.Success(it) }
    .catch { DatabaseState.Error(it.message) }
    .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)

Because of this declaration, your employeeById will be created when your view model is created, so employeeId.value is still null.
Then, because of SharingStarted.WhileSubscribed(). the map function will only get called when your flow has a subscriber on your repeatOnLifecycle(Lifecycle.State.STARTED). At this time, your employeeId.value is set the correct value. This is why you get a very weird log.

To fix your issue, I think some things need to be changed.

Your DAO

@Query(&quot;SELECT * FROM employees WHERE id = :id&quot;)
fun getEmployeeById(id: Int): Flow&lt;EmployeeModel?&gt;

Your viewModel. I assume that you have a state flow for your employee. You should use flatMap to update your employeeById value whenever your employee changes.

val employeeById: SharedFlow&lt;DatabaseState&lt;EmployeeModel&gt;&gt; = employeeId.filterNotNull().flatMapLatest {
   repository.get().getEmployeeById(it.toInt())
}.map {
    if (it!= null) DatabaseState.Success(it) else DatabaseState.Error(&quot;NOT FOUND&quot;)
}.catch { DatabaseState.Error(it.message) }
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 0)

Last thing, if you use employeeById to display data. Consider using StateFlow instead of SharedFlow

huangapple
  • 本文由 发表于 2023年3月31日 03:34:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/75892290.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定