架构 ViewModel 中的 BaseObservable 字段未添加任何观察者回调

BaseObservable field inside architecture ViewModel isn't getting any observer callback added

我正在研究新架构的 ViewModel 组件,打算将我的应用程序迁移到它,activity activity。我的应用程序已经使用了数据绑定,并且有一些模型对象作为 BaseObservable。目前,我有一个 activity 进行 API 调用,如果调用失败,我将恢复 UI 中的更改。我为此使用双向绑定。所以现在我为 activity 创建了一个 ViewModel class,并移动了一些业务逻辑和 API 调用它。执行此操作后,API 调用失败时的恢复序列不再更新 UI。我看到值更改到达我的 BaseObservable 模型,它调用 notifyChanged 方法,但是,模型有 mCallbacks == null,因此在模型更改后没有任何侦听器更新 UI。以下是作品:

Activity OnCreate

的一部分
this.dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_settings)
val viewModel = AppSettingsViewModel(this.application)
this.dataBinding.viewModel = viewModel
this.dataBinding.setLifecycleOwner(this)
this.lifecycle.addObserver(this.dataBinding.viewModel!!)
this.dataBinding.context = this

部分布局:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto">
   <data>
      <variable name="context" type="letstwinkle.com.twinkle.AppSettingsActivity" />
      <variable name="viewModel" type="letstwinkle.com.twinkle.viewmodel.AppSettingsViewModel"/>
      ...imports...
   </data>

   <androidx.drawerlayout.widget.DrawerLayout
      android:id="@+id/drawer"
      android:layout_width="match_parent" android:layout_height="match_parent"
      >

   ...more nested views...
                  <Switch
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:layout_marginTop="15dp"
                     android:layout_marginStart="12dp"
                     android:text="@string/new_matches"
                     android:checked="@={viewModel.settings.pnMatches}"
                     android:textColor="@color/settingsItem"
                     android:onClick="@{(v) -> viewModel.toggleNewMatches(((Checkable)v).isChecked)}"
                     android:enabled="@{viewModel.settings != null}"
                     />
</layout>

最后,ViewModel / SettingsModel

internal class AppSettingsViewModel(app: Application) : AndroidViewModel(app), VolleyErrorObservable,
    ResponseHandler<SubmitResult>, LifecycleObserver
{
    var settings: SettingsModel? = null
    val account = User.account
    val demoMessageText = MutableLiveData<String>()
    override val volleyError = MutableLiveData<VolleyError>()

    fun toggleNewMatches(newValue: Boolean) {
        this.twinkleApplication.trackEvent("click:togglesetting", "App Settings Toggle Setting",
                                           Bundle().apply { putString("setting", "pn_match") })
        updateBoolSetting("pn_match", newValue) { set, b -> set.pnMatches = b}
    }

    private fun updateBoolSetting(setting: String, newValue: Boolean, reverter: (SettingsModel, Boolean) -> Unit) {
        val obj = this.settings!!.jsonForSetting(setting, newValue)
        val suh = SettingsUpdateHandler(this.settings!!, setting == "mp_enabled")
            { settings -> reverter(settings, !newValue) }
        APIClient.updateSettings(obj, suh)
    }
    ...
}

private class SettingsUpdateHandler(val settings: SettingsModel,
                                    val isMPEnabled: Boolean = false,
                                    val revertFun: (SettingsModel) -> Unit)
    : ResponseHandler<SubmitResult>
{
    override fun onErrorResponse(error: VolleyError) {
        revertFun(settings)
        if (weakToast?.get()?.view?.isShown == true) {
            weakToast?.get()?.cancel()
        }
        val toast = Toast.makeText(TwinkleApplication.instance,
                                   R.string.failed_save_settings, Toast.LENGTH_LONG)
        ...
    }
    override fun onResponse(response: SubmitResult) {
        ...
    }

}

@Table(database = Database::class, useBooleanGetterSetters = false)
internal class SettingsModel() : BaseObservable(), Model {
    @NotNull @Column @get:Bindable var pnMatches: Boolean = false
        set(value) {
            Log.d("SettingsModel", "set pnMatches: value=$value, field=$field")
            val changed = field != value
            field = value
            notifyIfChanged(BR.pnMatches, changed)
        }
    ...
    private inline fun notifyIfChanged(field: Int, changed: Boolean) {
        if (changed)
            notifyPropertyChanged(field)
    }
}

我希望当 viewModel.settings.pnMatches 的值改回时 Switch 会切换回来,但事实并非如此。

我在其中一个数据绑定文档页面上找到了答案,其中包括对 ViewModel / LiveData 用法的讨论:ViewModel 需要自己实现 Observable,并在非 Observable 字段上使用 Bindable 注释实时数据:

internal class AppSettingsViewModel(app: Application) : AndroidViewModel(app), VolleyErrorObservable,
    ResponseHandler<SubmitResult>, NotifiableObservable by BaseNotifiableObservable()
{
    @get:Bindable
    var settings: SettingsModel? = null
.....

NotifiableObservable 位是我在网上找到的用于通过委托实现 Observable 的 Kotlin 习语。