MVVM // 在 Activity 旋转时触发 ViewModel 事件(重新创建)

Posted

技术标签:

【中文标题】MVVM // 在 Activity 旋转时触发 ViewModel 事件(重新创建)【英文标题】:MVVM // ViewModel event being fired on Activity rotation (recreated) 【发布时间】:2017-11-05 00:10:05 【问题描述】:

阅读Google docs,我发现(有点)一个使用selectedItem 的示例,以便将触发的事件传播给其他观察者,这是我当前的实现:

视图模型

public void onListItemClicked(Item item) 
    if (selectedItem.getValue() == item) 
        return;
    
    selectedItem.postValue(item);



public LiveData<Item> getSelectedItem() 
    if (selectedItem == null) 
        selectedItem = new MutableLiveData<>();
    

    return selectedItem;


查看

ListViewModel viewModel = ViewModelProviders.of(this).get(ListViewModel.class);

viewModel.getSelectedItem().observe(this, new Observer<Item>() 
    @Override
    public void onChanged(@Nullable Item item) 
        if (item != null) 
            openDetailActivity(item);
        
    
);

当用户点击列表时:

@Override
public void onItemClicked(Item item) 
    viewModel.onListItemClicked(item);

所有一切正常,问题是当用户旋转屏幕并重新创建ListActivity时检测到更改并打开@ 987654327@订阅时。

我找到了一种解决方法,即在 getSelectedItem() 上添加 selectedItem.postValue(null);,但这有点笨拙。

有人可能会争辩说,打开细节活动和传播事件应该是分开的,但我想知道是否有人有更好的实施/建议。

【问题讨论】:

【参考方案1】:

编辑

使用SingleLiveEvent 是可行的方法。这可以确保您的 ViewModel 只触发一次事件。

这是参考文章:

LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)

Java类可以在文章中找到

我用Kotlin 类创建了一个要点。 我一直在成功地使用这些用例:

Always up-to-date gist SingleLiveEvent.kt

我会及时更新要点,但我也会在此处保留代码 (仅供参考,这可能已经过时,因为我不会在每次更改要点时都编辑此答案):


package YOUR_PACKAGE


import androidx.annotation.MainThread
import androidx.annotation.Nullable
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean

/**
 * A lifecycle-aware observable that sends only new updates after subscription, used for events like
 * navigation and Snackbar messages.
 * <p>
 * This avoids a common problem with events: on configuration change (like rotation) an update
 * can be emitted if the observer is active. This LiveData only calls the observable if there's an
 * explicit call to setValue() or call().
 * <p>
 * Note that only one observer is going to be notified of changes.
 */
class SingleLiveEvent<T> : MutableLiveData<T>() 

    private val mPending = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) 
        // Observe the internal MutableLiveData
        super.observe(owner, Observer<T>  t ->
            if (mPending.compareAndSet(true, false)) 
                observer.onChanged(t)
            
        )
    

    @MainThread
    override fun setValue(@Nullable t: T?) 
        mPending.set(true)
        super.setValue(t)
    

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() 
        value = null
    



旧答案:

所以在进行了大量研究并与 Google 开发人员取得联系后。推荐的解决方案是有单独的职责。 如果响应点击事件而不是实际更改,则打开活动,这种类型的selectedItem 场景对于解耦通信与其他侦听视图特别有用。 例如同一活动中的另一个片段

【讨论】:

以上是关于MVVM // 在 Activity 旋转时触发 ViewModel 事件(重新创建)的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 上使用 MVVM 时,每个 Activity 是不是应该有一个(且只有一个)ViewModel?

Activity屏幕旋转时被销毁

安卓屏幕旋转时,禁止Activity重新加载

Android旋转屏幕时阻止activity重建

使用 MVVM 从 WPF ListView 项触发双击事件

当 Activity 固定为纵向时旋转生成的相机图像