我正在使用MVVM,并对其进行了不同的实现,但是有一件事仍然让我感到怀疑,那就是如何在不将任何生命周期附加到视图模型的情况下,从我的视图模型中获取存储库(Firebase)中的数据.

我已经从视图模型实现了observeForever(),但我认为这不是一个好主意,因为我认为我应该通过回调或转换从存储库与视图模型通信.

I leave here an example where I fetch a device from Firebase and update my 用户界面, if we can see here, I'm observing the data coming from the repo from the 用户界面, but from the 视图模型 I'm also observing data from the repo, and here is where I really doubt if I'm using the right approach, since I don't know if observeForever() will be cleared on onCleared() if my view is destroyed, so it won't keep the observer alive if the view dies.

用户界面

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            val deviceId = editText.text.toString().trim()
            observeData(deviceId)
        }
    }

    fun observeData(deviceId:String){
        viewModel.fetchDeviceData(deviceId).observe(this, Observer {
            textView.text = "Tipo: ${it.devType}"
        })

视图模型

class MainViewmodel: 视图模型() {

    private val repo = Repo()
    fun fetchDeviceData(deviceId:String):LiveData<Device>{
        val mutableData = MutableLiveData<Device>()
        repo.getDeviceData(deviceId).observeForever {
            mutableData.value = it
        }

        return mutableData
    }
}

Repository

class Repo {

    private val db = FirebaseDatabase.getInstance().reference
    fun getDeviceData(deviceId:String):LiveData<Device>{
        val mutableData = MutableLiveData<Device>()
        db.child(deviceId).child("config/device").addListenerForSingleValueEvent(object: ValueEventListener{

            override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val device = dataSnapshot.getValue(Device::class.java)
                    mutableData.value = device
            }

            override fun onCancelled(dataError: DatabaseError) {
                Log.e("Error","handle error callback")
            }
        })

        return mutableData
    }
}

这个例子只是展示了如何从Firebase获取设备,它是有效的,但是从我的视图模型来看,它让我一直认为observeForever()不是我要在存储库和视图模型之间进行数据通信的地方.

I have seen Transformations, but I, in this case, I just need to deliver the entire Device object to my 用户界面, so I don't need to transform the Object I'm retrieving to another Object

这里应该有什么正确的方法来正确地沟通存储库和视图模型?

推荐答案

是否可以永远观察生命周期?

No, that's why it's called observeForever.

I have implemented observeForever() from the ViewModel, but I don't think that is a good idea

不,不是,你应该用Transformations.switchMap {.

since I don't know if observeForever() will be cleared on onCleared() if my view is destroyed, so it won't keep the observer alive if the view dies.

Well if you're not clearing it in onCleared() using removeObserver(observer), then it won't clear itself, because it observes forever.

这就是我真的怀疑我使用的方法是否正确的地方,

不,通过被动的方法,你可以做得更好.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    button.setOnClickListener {
        val deviceId = editText.text.toString().trim()
        viewModel.onSelectedDeviceChanged(deviceId)
    }

    viewModel.selectedDevice.observe(this, Observer { device ->
        textView.text = "Tipo: ${device.devType}"
    })
}

class MainViewModel(
    private val savedStateHandle: SavedStateHandle,
): ViewModel() {
    private val repo = Repo() // TODO: move to Constructor Argument with ViewModelProvider.Factory

    private val selectedDeviceId: MutableLiveData<String> = savedStateHandle.getLiveData<String>("selectedDeviceId")

    fun onSelectedDeviceChanged(deviceId: String) {
        selectedDeviceId.value = deviceId
    }

    val selectedDevice = Transformations.switchMap(selectedDeviceId) { deviceId ->
        repo.getDeviceData(deviceId)
    }
}

class Repo {
    private val db = FirebaseDatabase.getInstance().reference // TODO: move to constructor arg? Probably

    fun getDeviceData(deviceId:String) : LiveData<Device> {
        return object: MutableLiveData<Device>() {
            private val mutableLiveData = this

            private var query: Query? = null
            private val listener: ValueEventListener = object: ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val device = dataSnapshot.getValue(Device::class.java)
                    mutableLiveData.value = device
                }

                override fun onCancelled(dataError: DatabaseError) {
                    Log.e("Error","handle error callback")
                }
            }

            override fun onActive() {
                query?.removeEventListener(listener)
                val query = db.child(deviceId).child("config/device")
                this.query = query
                query.addValueEventListener(listener)
            }
    
            override fun onInactive() {
                query?.removeEventListener(listener)
                query = null
            }
        }
    }
}

This way, you can observe for changes made in Firebase (and therefore be notified of future changes made to your values) using LiveData, rather than only execute a single fetch and then not be aware of changes made elsewhere to the same data.

Kotlin相关问答推荐

Kotlin-stdlib中的模拟扩展函数

Kotlin扩展函数未调用Hibernate/JPA中的重写函数

Kotlin 函数名后面的大括号是什么意思?

Kotlin Poet 的导入不明确

Kotlin 如何使用其 get 函数在内部检索映射值

gradle 如何 Select 以-jvm结尾的库?

我可以在 Kotlin 中使用接口类型作为构造函数参数吗

使用纯 Kotlin 函数作为 Junit5 方法源

Kotlin 插件错误:无法为类 org.jetbrains.kotlin.gradle.tasks.KotlinCompile 生成代理类

类型是什么意

在 Kotlin 中使用 @Parcelize 注释时如何忽略字段

来自类型参数的属性的自定义 getter

如何将 kotlin 集合作为 varagrs 传递?

Kotlin:什么是 kotlin.String!类型

如果我可以将 Flow 和 StateFlow 与生命周期范围 \ viewLifecycleOwner.lifecycleScope 一起使用,那么在 ViewModel 中使用 LiveData 有什么意义

Kotlin的web-framework框架

空对象引用上的 TransitionSet ArrayList.size()

Kotlin - 具有私有构造函数的类的工厂函数

通过在 kotlin-gradle 中使用子项目Unresolved reference: implementation

Kotlin - 如果不为空,则使用修改后的 Obj props 覆盖 Obj props