MVVM模式和startActivity [英] MVVM pattern and startActivity

查看:84
本文介绍了MVVM模式和startActivity的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近决定更仔细地研究Google发布的新的Android体系结构组件,特别是将其ViewModel生命周期感知类用于MVVM体系结构和LiveData.

I recently decided to have a closer look at the new Android Architecture Components that Google released, especially using their ViewModel lifecycle-aware class to a MVVM architecture, and LiveData.

只要我要处理一个Activity或一个Fragment,一切都很好.

As long as I'm dealing with a single Activity, or a single Fragment, everything is fine.

但是,我找不到一个很好的解决方案来处理活动切换. 举例来说,活动A具有启动活动B的按钮.

However, I can't find a nice solution to handle Activity switching. Say, for the sake of a short example, that Activity A has a button to launch Activity B.

在哪里处理startActivity()?

按照MVVM模式,clickListener的逻辑应位于ViewModel中.但是,我们希望避免在其中引用活动.因此,将上下文传递给ViewModel并不是一种选择.

Following the MVVM pattern, the logic of the clickListener should be in the ViewModel. However, we want to avoid having references to the Activity in there. So passing the context to the ViewModel is not an option.

我缩小了几个看起来确定"的选项,但找不到此处的操作方法"的正确答案.

I narrowed down a couple of options that seem "OK", but was not able to find any proper answer of "here's how to do it.".

选项1 :在ViewModel中有一个枚举,其值映射到可能的路由(ACTIVITY_B,ACTIVITY_C).结合使用LiveData. 该活动将观察此LiveData,并且当ViewModel决定应启动ACTIVITY_C时,它仅是postValue(ACTIVITY_C).然后,Activity可以正常调用startActivity().

Option 1 : Have an enum in the ViewModel with values mapping to possible routing (ACTIVITY_B, ACTIVITY_C). Couple this with a LiveData. The activity would observe this LiveData, and when the ViewModel decides that ACTIVITY_C should be launched, it'd just postValue(ACTIVITY_C). Activity can then call startActivity() normally.

选项2 :常规界面模式.与选项1相同的原理,但活动将实现该接口.不过,我对此感到更加满意.

Option 2 : The regular interface pattern. Same principle as option 1, but Activity would implement the interface. I feel a bit more coupling with this though.

选项3 :消息传递选项,例如Otto或类似内容. ViewModel发送一个Broadcast,Activity拾取它并启动它必须执行的操作.此解决方案的唯一问题是,默认情况下,应将该广播的注册/取消注册放入ViewModel中.因此无济于事.

Option 3 : Messaging option, such as Otto or similar. ViewModel sends a Broadcast, Activity picks it up and launches what it has to. Only problem with this solution is that, by default, you should put the register/unregister of that Broadcast inside the ViewModel. So doesn't help.

选项4 :在某个地方具有较大的路由类,例如单例或类似名称,可以调用该类以将相关路由分配给任何活动.最终通过接口?因此,每个活动(或BaseActivity)都将实现

Option 4 : Having a big Routing class, somewhere, as singleton or similar, that could be called to dispatch relevant routing to any activity. Eventually via interface? So every activity (or a BaseActivity) would implement

IRouting { void requestLaunchActivity(ACTIVITY_B); }

当您的应用开始有很多片段/活动时,此方法让我有些担心(因为路由类会变得笨拙)

This method just worries me a bit when your app starts having a lot of fragments/activities (because the Routing class would become humongous)

就这样.那是我的问题.你们如何处理? 您是否选择了我没有想到的选项? 您认为哪个选项最相关,为什么? 推荐的Google方法是什么?

So that's it. That's my question. How do you guys handle this? Do you go with an option that I didn't think of? What option do you consider the most relevant and why? What is the recommended Google approach?

PS:链接没有让我到任何地方 1- Android ViewModel调用Activity方法 2-如何从中开始活动一个普通的非活动Java类?

PS : Links that didn't get me anywhere 1 - Android ViewModel call Activity methods 2 - How to start an activity from a plain non-activity java class?

推荐答案

NSimon,它很好用,您开始使用AAC.

NSimon, its great that you start using AAC.

之前,我在aac的github中写了问题

I wrote a issue in the aac's-github before about that.

有几种方法.

一种解决方案是使用

WeakReference 到NavigationController其中包含活动的上下文.这是在ViewModel中处理上下文绑定内容的常用模式.

WeakReference to a NavigationController which holds the Context of the Activity. This is a common used pattern for handling context-bound stuff inside a ViewModel.

出于某些原因,我对此表示高度拒绝.首先:这通常意味着您必须保留对NavigationController的引用,该引用可以修复上下文泄漏,但根本无法解决体系结构.

I highly decline this for several reasons. First: that usually means that you have to keep a reference to your NavigationController which fixes the context leak, but doesnt solve the architecture at all.

(在我看来)最好的方法是使用LiveData,它具有生命周期意识,并且可以完成所有需要的工作.

The best way (in my oppinion) is using LiveData which is lifecycle aware and can do all the wanted stuff.

示例:

class YourVm : ViewModel() { 

    val uiEventLiveData = SingleLiveData<Pair<YourModel, Int>>()
    fun onClick(item: YourModel) {
        uiEventLiveData.value = item to 3 // can be predefined values
    }
}

之后,您可以在视图中收听更改.

After that you can listen inside your view for changes.

class YourFragmentOrActivity { 
     //assign your vm whatever
     override fun onActivityCreated(savedInstanceState: Bundle?) { 
        var context = this
        yourVm.uiEventLiveData.observe(this, Observer {
            when (it?.second) {
                1 -> { context.startActivity( ... ) }
                2 -> { .. } 
            }

        })
    }
}

请注意我使用的是经过修改的MutableLiveData,因为否则它将始终为新的Observers发出最新结果,从而导致不良行为.例如,如果您更改活动并返回,它将循环结束.

Take care that ive used a modified MutableLiveData, because else it will always emit the latest result for new Observers which leads to bad behaviour. For example if you change activity and go back it will end in a loop.

class SingleLiveData<T> : MutableLiveData<T>() {

    private val mPending = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }

        // Observe the internal MutableLiveData
        super.observe(owner, Observer { t ->
            if (mPending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

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

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

    companion object {
        private val TAG = "SingleLiveData"
    }
}

为什么尝试使用WeakReferences,Interfaces或任何其他解决方案更好?

因为此事件将UI逻辑与业务逻辑分开.也可以有多个观察者.它关心生命周期.它不会泄漏任何东西.

Because this event split UI logic with business logic. Its also possible to have multiple observers. It cares about the lifecycle. It doesnt leak anything.

您还可以通过使用PublishSubject使用RxJava而不是LiveData来解决它. (addTo需要 RxKotlin )

You could also solve it by using RxJava instead of LiveData by using a PublishSubject. (addTo requires RxKotlin)

请注意不要通过在onStop()中释放订阅来泄漏订阅.

Take care about not leaking a subscription by releasing it in onStop().

class YourVm : ViewModel() { 
   var subject : PublishSubject<YourItem>  = PublishSubject.create();
}

class YourFragmentOrActivityOrWhatever {
    var composite = CompositeDisposable() 
    onStart() { 
         YourVm.subject 
             .subscribe( { Log.d("...", "Event emitted $it") }, { error("Error occured $it") }) 
               .addTo(compositeDisposable)         
       }   
       onStop() {
         compositeDisposable.clear()
       }
    }

还要注意将ViewModel绑定到Activity或Fragment.您不能在多个活动之间共享ViewModel,因为这会破坏"Livecycle-Awareness".

Also take care that a ViewModel is bound to an Activity OR a Fragment. You can't share a ViewModel between multiple Activities since this would break the "Livecycle-Awareness".

如果您需要使用 room 或使用包裹共享数据.

If you need that persist your data by using a database like room or share the data using parcels.

这篇关于MVVM模式和startActivity的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆