【问题标题】:Navigation with MVVM android使用 MVVM 安卓导航
【发布时间】:2019-01-22 23:04:52
【问题描述】:

我有一个应用程序使用 Androids ViewModel 类和导航组件在片段之间导航。我将如何处理来自 ViewModel 的导航?我正在使用 RxJava,我正在考虑让 Fragments 监听导航事件,然后以这种方式触发导航。处理这个的正常方法是什么?如果有帮助,我也会使用 Dagger 进行依赖注入。

【问题讨论】:

    标签: android mvvm rx-java android-viewmodel android-architecture-navigation


    【解决方案1】:

    对于您的活动,一般而言,您可以使用以下内容:

    /**
     * Used as a wrapper for data that is exposed via a LiveData that represents an event.
     */
    open class Event<out T>(private val content: T) {
    
        @Suppress("MemberVisibilityCanBePrivate")
        var hasBeenHandled = false
            private set // Allow external read but not write
    
        /**
         * Returns the content and prevents its use again.
         */
        fun getContentIfNotHandled(): T? {
            return if (hasBeenHandled) {
                null
            } else {
                hasBeenHandled = true
                content
            }
        }
    
        /**
         * Returns the content, even if it's already been handled.
         */
        fun peekContent(): T = content
    }
    

    这样,您可以轻松地确保您曾经像这样使用过内容:

    viewModel.getShowAlertFragment().observe(viewLifecycleOwner, Event{
            event?.getContentIfNotHandled()?.let {
                // your code here
        }
    })
    

    但是为了让它更简单并且实际上避免在任何地方之前检查内容是否已被使用,您可以创建另一个扩展观察者的类并在那里进行检查:

    /**
     * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
     * already been handled.
     *
     * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
     */
    class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
        override fun onChanged(event: Event<T>?) {
            event?.getContentIfNotHandled()?.let {
                onEventUnhandledContent(it)
            }
        }
    }
    

    现在有了这个,你可以把你的代码简化成这样:

    viewModel.getShowAlertFragment().observe(viewLifecycleOwner, EventObserver {
            // do you stuff here and you know it will only be called once
        }
    })
    

    这个想法来自Android示例Here中的这个文件

    【讨论】:

      【解决方案2】:

      如果您使用 MVP,P 只会调用 V 上触发导航的方法。

      MVVM 中的等效方法是有一个专用的 observable/listener/callback,如果使用 RxJava,它可以由 PublishSubject 驱动。这满足一次性要求。相反,如果您需要响应订阅之前可能发出的事件,您可以使用BehaviorSubject&lt;Optional&lt;T&gt;&gt; 并使用createDefault(absent&lt;T&gt;()) 创建它,使用onNext(Optional.of(navigationObject)) 触发它,然后让VM 知道何时发生导航,然后VM可以用onNext(absent())清除它

      或者,如果你想把它变成一些包罗万象的 redux/mvi 状态,你可能有一些状态类,其中包括一些属性,指示视图在接收/操作时导航到某处视图会告诉虚拟机它已经这样做了,虚拟机会将状态设置为与当前相同但没有导航。例如(在 Kotlin 中)state = state.copy(navigateToX = false)

      【讨论】:

        【解决方案3】:

        根据LiveData with SnackBar, Navigation, and other events blog post

        有些数据应该只被使用一次,例如 Snackbar 消息、导航事件或对话框触发器。

        与其尝试通过库或架构组件的扩展来解决此问题,不如将其视为设计问题。 我们建议您将活动视为您所在州的一部分。

        他们详细介绍了 SingleLiveEvent 类的使用,该类确保每个 Navigation 事件仅由 Observer 接收一次(即,您的 Fragment 可以访问您的 NavController)。

        另一种选择是使用“事件包装器”模型,其中必须将事件显式标记为已处理。

        【讨论】:

        • 虽然我同意这是官方的建议,但我认为谷歌在这里犯了一个大错误。导航不是特定视图模型“状态”的一部分,也不是事件。它是它自己的实体(我喜欢将其视为一种服务),它可能包含它自己的状态和事件。他们需要为此发明一个 SingleLiveEvent 的事实就是证明。
        • 如果我在当前屏幕上有很多可能的目的地。对于每个可能的目的地,我是否应该有 1 个 SingleLiveEventLiveDataEvent Wrapper?这方面的最佳做法是什么?
        • medium.com/google-developer-experts/… 有一个从 ViewModel 发布导航事件的干净实现
        • SingleLiveData 不是反模式吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多