mvi架构_那么为什么我们在移动开发中需要mvi

mvi架构

I assume that you already have heard a lot of about MVI, how to cook and configure it well. But not so many articles and lectures about how MVI simplify life of mobile developer in comparison with other MV* patterns.

我假设您已经听说过很多有关MVI的知识,以及如何正确烹饪和配置它。 但是,与其他MV *模式相比,关于MVI如何简化移动开发人员寿命的文章和讲座并不多。

本文的主要目的 (The main purpose of this article)

I will not deep dive into technical realization of MVI (there are many ways to do it and every way has its own pluses and minuses). My main purpose is to arouse your interest in this theme, my friend. Maybe in the future you will use MVI in your production projects :)

我不会深入探讨MVI的技术实现(有很多方法可以实现,每种方法都有其优缺点)。 我的主要目的是引起您对这个主题的兴趣,我的朋友。 也许将来您会在生产项目中使用MVI :)

您可以面对哪个问题 (Which problem you can face)

My dear friend, lets imagine the situation where you have interface of the view looking like:

我亲爱的朋友,让我们想象一下您拥有视图界面的情况,如下所示:

interface ComplexView {  fun showLoading()  fun hideLoading()  fun showBanner()  fun hideBanner()  fun dataLoaded(names: List<String>)  fun showTakeCreditDialog()  fun hideTakeCreditDialog()}

In a first glance it seems that there is nothing hard to understand. You can just call these view methods to do whatever you need to do. Let’s assume that you have entity which can manage all view needs. And you give it a name like presenter:

乍一看似乎没有什么难理解的。 您只需调用这些视图方法即可执行所需的任何操作。 假设您拥有可以管理所有视图需求的实体。 您给它起了一个像主持人的名字:

interface Presenter {  fun onLoadData(dataKey: String)  fun onLoadCredit()}

BUT:

但:

Image for post

View can call presenter methods when its need to load data, and presenter can call view methods to show some results. But we have one problem here, my friend. It is lack of control for your UI state. I’ll try to explain.

视图可以在需要加载数据时调用演示者方法,演示者可以调用视图方法以显示一些结果。 但是,我的朋友,我们这里有一个问题。 您的UI状态缺乏控制。 我会尽力解释。

For example, you want to show dialog to user and you can call view method from the presenter like that:

例如,您想向用户显示对话框,您可以像这样从演示者调用view方法:

view.showTakeCreditDialog() (view.showTakeCreditDialog())

But you should remember that when you show dialog, you have to hide loading and you can not show loading while you have dialog on your screen. Plus for that you have method which can show banner on the view. And you can not show banner while you have dialog as well as you can not show dialog while you have banner on the screen. So you have situation like that:

但是您应该记住,显示对话框时,您必须隐藏加载,并且在屏幕上显示对话框时不能显示加载。 此外,您还有可以在视图上显示横幅的方法。 并且当您有对话框时,您将无法显示横幅;当您在屏幕上时,您将无法显示对话框。 所以你有这样的情况:

You shouldn’t call these methods in any way while you showing the dialog:

显示对话框时,请勿以任何方式调用这些方法:

v̵i̵e̵w̵.̵s̵h̵o̵w̵B̵a̵n̵n̵e̵r̵(̵)̵ (v̵i̵e̵w̵.̵s̵h̵o̵w̵B̵a̵n̵n̵e̵r̵(̵)̵)

v̵i̵e̵w̵.̵s̵h̵o̵w̵L̵o̵a̵d̵i̵n̵g̵(̵)̵ (v̵i̵e̵w̵.̵s̵h̵o̵w̵L̵o̵a̵d̵i̵n̵g̵(̵)̵)

Because if you call this method, you will have inconsistent UI state. And your tester will cry from the fact that he sees such beauty when sale banner is showing above important dialog with a great offer.

因为如果调用此方法,则UI状态将不一致。 当销售标语以重要报价显示在重要对话框上方时,您的测试人员会因为看到如此美丽而哭泣。

So, you have to remember that when you call this method:

因此,您必须记住在调用此方法时:

view.showBanner() (view.showBanner())

It is important to call:

重要的是要致电:

view.hideLoading() (view.hideLoading())

view.hideTakeCreditDialog() (view.hideTakeCreditDialog())

That is all again for consistency. So maybe you have question right now. Who will tell you that you are doing something wrong ? And the answer is simple NOBODY will. In current realization you have no control.

一切都是为了保持一致性。 所以也许您现在有疑问。 谁会告诉您您做错了什么? 答案很简单,那就是NOBODY。 在当前的实现中,您无法控制。

Maybe in the future you will have to add new view functionality, logically connected with the existing functionality. So which minuses do we have here ?

也许将来您将不得不添加新的视图功能,并与现有功能进行逻辑连接。 那么,我们在这里有哪些缺点呢?

  1. We have logic of changing state of the view in the whole presenter and it’s hard to see them all at once.

    我们有改变整个演示者的视图状态的逻辑,很难一次看到它们。
  2. Dependency spaghetti of UI elements.

    UI元素的依赖性意大利面条。
  3. It is really hard to add new UI state to your screen, because the risk is too great, you can forget to hide some dialogs, loaders, etc. Or forget to show some UI elements.

    确实很难在屏幕上添加新的UI状态,因为风险太大,您可以忘记隐藏一些对话框,加载程序等。或者忘记显示一些UI元素。

We have here view with only 7 METHODS. And even in that case we have problems.

这里只有7个方法。 即使在这种情况下,我们也有问题。

Sometimes in the wild nature we can have view like that:

有时,在野外自然环境中,我们可以看到这样的视图:

interface ChatView : IView<ChatPresenter> {

fun initImageInChat(needImageInChat: Boolean)

fun enableNavigationButton()

fun hideKeyboard()

fun scrollToFirstMessage()

fun setTitle(@StringRes titleRes: Int)

fun setVisibleSendingError(isVisible: Boolean)

fun removeMessage(localId: String)

fun setBottomPadding(hasPadding: Boolean)

fun initMessagesList(pageSize: Int)

fun showToast(@StringRes textRes: Int)

fun openMessageDialog(message: String)

fun showSuccessRating()

fun setRatingAvailability(isEnabled: Boolean)

fun showSuccessRatingWithResult(ratingValue: String)
}

It is really too hard add something new to this view. You have to watch how each method depends on other methods. And you can just start to work with tears in your eyes. And in that moment you can find him.

在此视图中添加新内容确实太难了。 您必须注意每种方法如何依赖于其他方法。 然后,您就可以开始流泪了。 在那一刻,你可以找到他。

MVI (MVI)

Image for post

The whole essence of this principle is that we have entity, which is called STATE. I will not deep dive into MVI, because my goal is to awaken your interest, so let’s go to the examples. There are list of useful sources in the end of the article.

该原则的全部实质是我们拥有称为STATE的实体。 我不会深入研究MVI,因为我的目标是唤起您的兴趣,所以让我们看一下示例。 本文末尾列出了有用的资源。

Let’s remember our situation in the beginning of current article. Where we were showing dialogs, banners a̵n̵d̵ ̵m̵a̵g̵i̵c̵ . Now you can describe state of the view:

让我们在本文开头记住我们的情况。 在显示对话框的地方,横幅a̵n̵d̵̵m̵a̵g̵i̵c̵。 现在您可以描述视图的状态:

data class UIState(
val loading: Boolean = false,
val names: List<String>? = null,
val isBannerShowing: Boolean = false,
val isCreditDialogShowing: Boolean = false
)

Let’s set the rule. You can change view only by this state class. So we have interface like this:

让我们设置规则。 您只能通过此状态类更改视图。 所以我们有这样的界面:

interface ComplexView {   
fun renderState(state: UIState)
}

And now we set one more rule (so many rules, but is’s required). You can contact the owner of the state only at one entry point, by sending him something we can call ACTIONS:

现在,我们再设置一个规则(很多规则,但这是必需的)。 您只能在一个入口点与国家所有者联系,方法是向他发送我们可以称为ACTIONS的信息:

sealed class UiAction {
class LoadNamesAction(dataKey: String) : UiAction()
object LoadBannerAction : UiAction()
object LoadCreditDialogInfo : UiAction()
}

Please, don’t throw tomatoes at me because of the sealed class. This language feature will help you to get rid of redundant casts when processing actions. So here you have interface for the presenter like that:

拜托,不要因为密封课而向我扔西红柿。 此语言功能将帮助您在处理动作时摆脱多余的演员。 因此,在这里您可以为演示者提供如下界面:

interface Presenter {    
fun processAction(action: UIAction)
}

So now you have to link all of these things:

因此,现在您必须链接所有这些内容:

fun processAction(action: UiAction): UIState {
return when (action) {
is UiAction.LoadNamesAction -> state.copy(
loading = true,
isBannerShowing = false,
isCreditDialogShowing = false
)
is UiAction.LoadBannerAction -> state.copy(
loading = false,
isBannerShowing = true,
isCreditDialogShowing = false
)
is UiAction.LoadCreditDialogInfo -> state.copy(
loading = false,
isBannerShowing = false,
isCreditDialogShowing = true
)
}
}

If you paid attention, the process of changing from one state to another is now happening in one place and it is already easier to put together a picture of how everything works in your head.

如果您注意的话,从一种状态转换为另一种状态的过程现在正在一个地方进行,将所有事情如何在脑海中运转起来,已经很容易了。

Now it’s not super easy, but your life should be easier. Plus, in my example, this is not visible, but we can decide how to process our new state based on the previous state (there are also several ways to implement this). And I did not say about insane reusability that the guys at badoo achieved. MVI helped them with this.

现在,这并非易事,但您的生活应该会更轻松。 另外,在我的示例中,这是不可见的,但是我们可以根据先前的状态(还有几种实现此状态的方法)来决定如何处理新状态。 我没有说badoo的家伙实现了疯狂的可重用性。 MVI为此提供了帮助。

但是,您不应该过早地为之欢欣,这个世界上的所有事物都有利弊,而这就是: (However, you should not rejoice early, everything in this world has both pros and cons, and here they are:)

  1. You can not show simple toast without pain it the head a̵s̵s̵

    你不能表现简单的吐司没有头疼
  2. When you update one field of the state class, the entire state will be copied again and sent to view, unnecessary redrawing will occur if nothing is done about it

    当您更新状态类的一个字段时,整个状态将再次被复制并发送给视图,如果不对其进行任何处理,则会发生不必要的重绘

Let’s assume that we want to display a regular android toast, according to the current logic, we will set a flag in our state for displaying our toast.

假设我们要显示常规的Android Toast,根据当前逻辑,我们将在状态下设置一个标志以显示Toast。

data class UIState(
val showToast: Boolean = false
)

首先 (Firstly)

Ok, you change state of presenter and setting showToast = true and the simplest thing which could happen is phone screen rotation. All is destroying, e̵x̵p̵l̵o̵s̵i̵o̵n̵s̵ ̵a̵n̵d̵ ̵d̵e̵s̵t̵r̵u̵c̵t̵i̵o̵n̵s, activity is recreating. But you are the really cool developer, so your state is lifecycle aware. But you have the flag showToast which is true. As a result toast is showing twice. There are several ways to solve this problem, a̵n̵d̵ ̵t̵h̵e̵y̵ ̵a̵l̵l̵ ̵l̵o̵o̵k̵ ̵l̵i̵k̵e̵ ̵c̵r̵u̵t̵c̵h̵e̵s̵. Again, this will be written in the sources attached to this article.

好的,您更改演示者的状态并设置showToast = true,可能发生的最简单的事情是手机屏幕旋转。 一切都在破坏,e̵x̵p̵l̵o̵s̵i̵o̵n̵s̵̵a̵n̵d̵̵d̵e̵s̵t̵r̵u̵c̵t̵i̵o̵n̵s,活动正在重新创造。 但是您是一个非常酷的开发人员,因此您的状态是生命周期感知的。 但是您拥有的标志是showToast,这是真的。 结果,吐司显示了两次。 解决此问题的方法有几种,一种是aln̵d̵t̵h̵e̵y̵a̵l̵l̵l̵o̵o̵o̵k̵̵l̵i̵k̵e̵c̵r̵u̵t̵c̵h̵e̵s̵。 同样,这将写在本文所附的资源中。

其次 (Secondly)

This is a problem of unnecessary rendering in the view, which will occur every time when only one of the fields in the state changes. And this problem is solved in several sometimes not the most beautiful ways (by a dull check before set a new value to the state, that it is different from the previous one). But with the release of compose in a stable version, this problem will be solved, then my friend we will live with you in a transformed and happy world!

这是视图中不必要渲染的问题,每次状态中只有一个字段发生更改时,都会发生这种渲染。 这个问题可以通过几种有时不是最漂亮的方法来解决(通过在设置新值到状态之前进行呆板检查,该值不同于先前的值)。 但是随着compose稳定版本的发布,这个问题将得到解决,然后我的朋友,我们将与您一起生活在一个转变而幸福的世界中!

现在是时候了: (It is time for the pluses:)

  1. One entry point to the view

    视图的一个入口点
  2. We always have current state of the screen at hand

    我们总是拥有屏幕的当前状态
  3. At the implementation stage, you have to think about how one state will flow in the other and what is the connection between them

    在实施阶段,您必须考虑一种状态如何流入另一种状态以及它们之间的联系是什么
  4. Unidirectional Data Flow

    单向数据流
Love android and never lose your motivation!

我的启发者名单: (List of my inspirers:)

翻译自: https://medium.com/@seangares13/so-why-do-we-need-mvi-in-mobile-development-3bd467b29841

mvi架构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值