我尝试使用mvvm +数据绑定+ livedata进行练习 但陷入无限循环。 请告诉我如何解决它,谢谢!
这是我的代码:
<?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])
}
})
}
}
答案 0 :(得分:0)
由于在观察者函数内部,该函数观察ViewModel中的实时数据,因此您可以立即设置新值来更新所有观察者。这是无限循环。您不应从视图更新实时数据变量。视图(片段,活动)应仅观察数据。
viewModel.dataItem.observe(this,
Observer<DataItem> { dataItem ->
Log.d("Huang", " MainActivity Observer " + dataItem.firstName)
// Problem is below
// viewModel.dataItem.value = dataItem
})
答案 1 :(得分:0)
您正在更新其回调中的观察者数据,因此只要调用 viewModel.dataItem.value = dataItem 它给您回调并陷入无限循环
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 :(得分:0)
我还遇到了使用 ViewModel 和 Databinding 库时遇到的问题。我通过以下方法解决了这个问题。
class SplashViewModel @Inject constructor(private val apiRepository: ApiRepository) : ViewModel() {
private val _isLogin = MutableLiveData<LoginResultData>()
val isLogin: LiveData<LoginResultData> = _isLogin
fun login(mobile:String){
viewModelScope.launch(networkExceptionHandler) {
var login = apiRepository.login(mobile)
_isLogin.value = login
showLog("login : "+login)
}
}
}
实际上,当发出新值时,您需要设置该值,而忽略之前的值(如果保持不变)。