【问题标题】:ViewModel onChanged gets infinite loopViewModel onChanged 获取无限循环
【发布时间】:2019-12-17 11:19:16
【问题描述】:

我尝试使用 mvvm + databinding + livedata 进行练习 但进入无限循环。 请告诉我如何解决它,谢谢!

这是我的代码:

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.guanhong.mvvmpractice.viewmodel.MainViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".view.MainActivity">

        <TextView
            android:id="@+id/name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:gravity="center"
            android:onClick="onViewClick"
            android:text="@{@string/player_name(viewModel.dataItem.firstName, viewModel.dataItem.lastName)}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/heightFeet"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:gravity="center"
            android:onClick="onViewClick"
            android:text="@{@string/high_feet(viewModel.dataItem.heightFeet.toString())}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/name" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

主要活动

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding
    lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this

        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        binding.viewModel = viewModel

        viewModel.dataItem.observe(this,
            Observer<DataItem> { dataItem ->

                Log.d("Huang", " MainActivity Observer " + dataItem.firstName)

                viewModel.dataItem.value = dataItem
            })

        viewModel.init()
    }
}

存储库

class MainRepository {

    fun getAllPlayer(callback: GetAllPlayerCallback) {

        val retrofit = Retrofit
            .Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl("https://free-nba.p.rapidapi.com/")
            .build()

        val allPlayerData = retrofit.create(AllPlayerApi::class.java)

        val call = allPlayerData.getAllPlayer(2)

        call.enqueue(object : Callback<AllPlayerData> {
            override fun onFailure(call: Call<AllPlayerData>?, t: Throwable?) {
                Log.d("Huang", " MainRepository get player fail ")
            }

            override fun onResponse(call: Call<AllPlayerData>?, response: Response<AllPlayerData>) {

                Log.d("Huang", " MainRepository onResponse ")

                callback.onSuccess(response.body()!!.data!!)
            }
        })
    }
}

视图模型

class MainViewModel : ViewModel() {

    private val repository = MainRepository()

    var dataItem = MutableLiveData<DataItem>()

    fun init() {
        getAllPlayer()
    }

    private fun getAllPlayer() {

        repository.getAllPlayer(object : GetAllPlayerCallback {
            override fun onSuccess(dataItemList: List<DataItem>) {

                Log.d("Huang", " MainViewModel getAllPlayer onSuccess ")

                dataItem.value = (dataItemList[0])
            }
        })
    }
}

Logcat

logcat

【问题讨论】:

  • 你是怎么解决的?我在使用数据绑定时也遇到了这个问题。

标签: android kotlin mvvm data-binding android-livedata


【解决方案1】:

您正在其回调中更新观察者数据,因此只要您调用 viewModel.dataItem.value = 数据项 它给你一个回调并陷入无限循环

viewModel.dataItem.observe(this,
            Observer<DataItem> { dataItem ->

                Log.d("Huang", " MainActivity Observer " + dataItem.firstName)

                viewModel.dataItem.value = dataItem
            })viewModel.dataItem.observe(this,
            Observer<DataItem> { dataItem ->

                Log.d("Huang", " MainActivity Observer " + dataItem.firstName)

                viewModel.dataItem.value = dataItem
            })

【讨论】:

  • 你是对的!!但是如果我使用数据绑定,我应该更新我的视图模型,我该怎么做。
【解决方案2】:

因为在观察者函数内部,观察来自 ViewModel 的实时数据,您会立即设置更新所有观察者的新值。这是无限循环。您不应从视图中更新实时数据变量。视图(片段、活动)应该只观察数据。

viewModel.dataItem.observe(this,
            Observer<DataItem> { dataItem ->

                Log.d("Huang", " MainActivity Observer " + dataItem.firstName)

                // Problem is below
                // viewModel.dataItem.value = dataItem
            })

【讨论】:

  • 你是对的!!但是如果我使用数据绑定,我应该更新我的视图模型,我该怎么做。
  • 取决于更新视图模型的用户交互类型。例如,对于 MaterialButton,您可以有类似 mBinding.myButton.onClickListener = someOnClickListenerDefinition 的内容,并且 someOnClickListenerDefinition 将调用 viewModel.doSomethingThatUpdatesViewModel
【解决方案3】:

我也遇到了您在使用 ViewModelDatabinding 库时遇到的问题。我通过以下方法解决了这个问题。

  viewModel.dataItem.observe(this,
        Observer<DataItem> { dataItem ->
            //This condition will actually prevent the infinite loop
            if(!(viewModel.dataItem.value?.equals(dataItem))) 
            viewModel.dataItem.value = dataItem
        })

实际上,您需要在发出新值时设置该值,如果之前的值保持不变,则忽略它。

【讨论】:

    猜你喜欢
    • 2020-09-09
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 2014-03-13
    • 2021-11-03
    • 1970-01-01
    • 2020-07-16
    相关资源
    最近更新 更多