只需添加轨道2的mvi

    科技2023-12-05  101

    We love the MVI architecture concept.

    我们喜欢MVI架构概念。

    Fragmented Podcast: MVI pattern with Hannes Mosby Dorfmann

    片段播客:Hannes Mosby Dorfmann的MVI模式

    Why MVI?

    为什么选择MVI?

    It brings clarity and consistency to our codebases. We wish to share our best knowledge of simple, no-nonsense MVI for Kotlin, Android and beyond. It fits both small and large projects well. We hope you find the library we created useful to you and makes your life a bit easier.

    它为我们的代码库带来了清晰度和一致性。 我们希望分享有关Kotlin,Android和其他应用程序的简单,轻松的MVI的最佳知识。 它非常适合大型和小型项目。 我们希望您发现我们创建的库对您有用,并使您的生活更轻松。

    We spent the last few months building a much-improved version of our MVI library, Orbit 2, taking the best features of Orbit 1 and rewriting the rest from scratch.

    在过去的几个月中,我们花了很多时间对MVI库Orbit 2进行了改进,以利用Orbit 1的最佳功能并从头开始重写其余内容。

    Let’s jump straight into the action to show you how easy it is to build MVI into your application. We will load a list of posts to form a Twitter-like feed.

    让我们直接进行操作,向您展示将MVI构建到应用程序中有多么容易。 我们将加载帖子列表以形成类似Twitter的feed。

    创建一个轨道ViewModel (Creating an Orbit ViewModel)

    First we include the Orbit library in Gradle:

    首先,我们在Gradle中包含Orbit库:

    implementation("com.babylon.orbit2:orbit-viewmodel:1.0.0")

    To get an Orbit-enabled ViewModel, implement the ContainerHost interface. Then, use one of our handy factory functions to create the Orbit container.

    要获得启用了Orbit的ViewModel ,请实现ContainerHost接口。 然后,使用我们方便的工厂功能之一创建Orbit容器。

    data class PostListState( val overviews: List<PostOverview> = emptyList() ) class PostListViewModel() : ViewModel(), ContainerHost<PostListState, Nothing> { override val container = container<PostListState, NavigationEvent>(PostListState()) }

    Now let’s load some data.

    现在让我们加载一些数据。

    在容器创建时加载数据 (Loading data on container creation)

    A typical use case on Android would be to run an initial request to load data when you create the container for the first time. Orbit supports this use case through a lambda you pass to the container factory function:

    在Android上,典型的用例是在首次创建容器时运行初始请求以加载数据。 Orbit通过传递给容器工厂函数的lambda支持此用例:

    class PostListViewModel() : ViewModel(), ContainerHost<PostListState, Nothing> { override val container = container<PostListState, NavigationEvent>(PostListState()) { loadOverviews() } fun loadOverviews() = ... }

    After setting that up, we can invoke Orbit to launch a network request.

    设置好之后,我们可以调用Orbit发起网络请求。

    fun loadOverviews() = orbit { transform { postRepository.getOverviewsBlocking() }.reduce { state.copy(overviews = event) } }

    Let’s unpack what happened here. We can distinguish Orbit operators used:

    让我们打开这里发生的事情。 我们可以区分使用的轨道运算符:

    The orbit block allows you to build and run MVI flows when you call a function.

    调用函数时, orbit块允许您构建和运行MVI流。

    The transform operator allows you to map upstream events. In this case, there is no upstream, so all we do is make a blocking network call. All transform operators execute on an IO thread pool.

    transform运算符使您可以映射上游事件。 在这种情况下,没有上游,因此我们要做的只是进行阻塞的网络呼叫。 所有转换运算符都在IO线程池上执行。

    The reduce operator reduces upstream events with the current state of the container. The result is a new container state.

    reduce运算符使用容器的当前状态来减少上游事件。 结果是一个新的容器状态。

    We can optimise this further but for now, let’s connect our UI to the ViewModel.

    我们可以进一步优化它,但是现在,让我们将UI连接到ViewModel 。

    将ViewModel连接到Android UI (Connecting the ViewModel to Android UI)

    The easiest way to connect our ViewModel is via LiveData support. We provide this through an extension module.

    连接ViewModel的最简单方法是通过LiveData支持。 我们通过扩展模块提供此功能。

    implementation("com.babylon.orbit2:orbit-livedata:1.0.0") class PostListFragment : Fragment() { // Injecting the ViewModel via Koin private val viewModel: PostListViewModel by stateViewModel() override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val adapter = PostListAdapter() // Configure the RecyclerView content.apply { layoutManager = LinearLayoutManager(activity) adapter = adapter } // Connect to the ViewModel via LiveData viewModel.container.state.observe( viewLifecycleOwner, Observer { adapter.update(it.overviews.map { PostListItem(it, viewModel) }) } ) } ... }

    Invoking actions on our MVI system from the UI is easy - just call a function on your ViewModel. As an example, we will make the pull to refresh gesture load the overviews again:

    从UI调用MVI系统上的动作很容易-只需在ViewModel上调用一个函数即可。 作为示例,我们将使刷新刷新手势再次加载概述:

    class PostListFragment : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { ... mySwipeRefreshLayout.setOnRefreshListener { viewModel.loadOverviews() } } }

    Your ViewModel functions are plain old functions, so you can pass parameters through if needed as normal. Your MVI flows can then use these parameters.

    您的ViewModel函数是普通的旧函数,因此您可以像往常一样传递参数。 然后,您的MVI流可以使用这些参数。

    优化ViewModel (Optimising the ViewModel)

    Blocking calls, even offloaded to a background thread, are not the best practice. Fortunately, the Orbit library is extensible and modular. We provide extra operators via modules.

    阻止调用,甚至卸载到后台线程,也不是最佳实践。 幸运的是,Orbit库是可扩展的和模块化的。 我们通过模块提供额外的运算符。

    implementation("com.babylon.orbit2:orbit-coroutines:1.0.0") implementation("com.babylon.orbit2:orbit-rxjava1:1.0.0") implementation("com.babylon.orbit2:orbit-rxjava2:1.0.0") implementation("com.babylon.orbit2:orbit-rxjava3:1.0.0") class PostListViewModel( private val postRepository: PostRepository ) : ViewModel(), ContainerHost<PostListState, Nothing> { ... // Coroutines fun loadOverviews() = orbit { transformSuspend { postRepository.getOverviews() }.reduce { state.copy(overviews = event) } } // Coroutines Flow fun loadOverviews() = orbit { transformFlow { postRepository.getOverviews() }.reduce { state.copy(overviews = event) } } // RxJava3 fun loadOverviews() = orbit { transformRx3Single { postRepository.getOverviews() }.reduce { state.copy(overviews = event) } } }

    轨道2设计 (Orbit 2 design)

    We have big ambitions for Orbit. Yet, the design decisions we made when we first wrote what we now call Orbit 1 held us back from achieving our goals. Above all, we value simplicity and developer experience. While devs loved working with Orbit 1, we felt we could simplify it further. Testing was also another area of improvement due to it being quite cumbersome.

    我们对Orbit抱负很大。 但是,当我们第一次编写现在称为Orbit 1的设计决策时,使我们无法实现目标。 最重要的是,我们重视简单性和开发人员经验。 尽管开发人员喜欢使用Orbit 1,但我们仍可以进一步简化它。 由于测试相当麻烦,因此它也是另一个需要改进的地方。

    Photo by Marc Reichelt on Unsplash Marc Reichelt在 Unsplash上 拍摄的照片

    Times have moved on. Coroutines are now officially recommended by Google for asynchronicity in Android. We just had to support them in Orbit. At the same time, we did not want to lose RxJava compatibility. The old architecture meant having both was not possible.

    时代在前进。 Google现已正式推荐协程,以实现Android中的异步性。 我们只需要在Orbit中支持它们即可。 同时,我们不想失去RxJava的兼容性。 旧的体系结构意味着不可能两者兼而有之。

    One fundamental shift in Orbit 2 was doing away with using streams for everything. We can represent the MVI concept via one big reactive cycle. In that cycle, network requests are mid-cycle transformations. The suggestion for RxJava MVI implementations was to use ObservableTransformers. That is, in our opinion, overcomplicated. The reality is that 95% of use cases only ever emit one result. For this, you do not need streams. Why make 100% of your code complex to support 5% of corner cases?

    Orbit 2的一项根本转变是取消使用流进行所有操作。 我们可以通过一个大的React周期来表示MVI概念。 在那个周期中,网络请求是周期中的转换。 对于RxJava MVI实现的建议是使用ObservableTransformers 。 我们认为这就是过于复杂。 现实情况是,95%的用例只能产生一个结果。 为此,您不需要流。 为什么要使100%的代码复杂以支持5%的极端情况?

    The most common use case is that one UI action sets in motion a one-off MVI flow (e.g. a network request). Orbit 2 makes that use case the primary focus to inform the developer experience. Worry not, we still allow you to connect streams where you need them.

    最常见的用例是,一个UI动作会启动一个一次性的MVI流(例如,网络请求)。 Orbit 2使该用例成为通知开发人员体验的主要重点。 不用担心,我们仍然允许您在需要它们的地方连接流。

    The result is a clear and concise syntax with 1–1 mapping between UI actions and MVI flows.

    结果是UI动作和MVI流之间具有1-1映射的清晰简洁的语法。

    MVI architecture can describe complex systems in simple terms. Indeed, we don’t think we should limit its usefulness to UI. The platform you use it on should not be a limitation either.

    MVI体系结构可以用简单的术语描述复杂的系统。 确实,我们不认为我们应该将其用处仅限于UI。 您使用它的平台也不应该受到限制。

    We currently support only Kotlin and Android projects. The JetBrains team has put hard work into making Coroutines multiplatform. That enables us to consider taking Orbit to other platforms.

    我们目前仅支持Kotlin和Android项目。 JetBrains团队为使协程成为多平台付出了艰辛的工作。 这使我们可以考虑将Orbit带到其他平台。

    All the pain points above guided us to Kotlin Coroutines as the basis for Orbit 2.

    以上所有痛苦点都将我们引导到Kotlin Coroutines,作为“轨道2”的基础。

    结语 (Wrapping up)

    We hope we piqued your interest and you found this post informative. That was just a taste of Orbit’s capabilities!

    希望我们引起了您的兴趣,并且您发现此信息有益。 那只是对Orbit功能的一种体验!

    Orbit 2 in a nutshell:

    简而言之,轨道2:

    Integrates best practices from our 2+ years of experience with MVI

    结合我们2年以上的MVI经验中的最佳实践

    Powered by Coroutines

    由协程提供动力

    Easy to use, type-safe, extensible API

    易于使用,类型安全,可扩展的API

    Works with any async/stream framework

    适用于任何异步/流框架

    Coroutine, RxJava (1, 2 & 3) and LiveData operator support

    Coroutine , RxJava (1、2和3)和LiveData运算符支持

    Create yours if needed

    根据需要创建自己的

    Orbit ❤️ Android

    轨道❤️Android

    Subscribe to state and side effects through LiveData

    通过LiveData订阅状态和副作用

    ViewModel support, along with SavedState

    ViewModel支持以及SavedState

    Testing

    测试中

    Unit test framework designed in step with the framework

    与框架同步设计的单元测试框架

    Built-in Espresso idling resource support

    内置的意式浓缩咖啡空闲资源支持

    Documentation and more complete samples are available below

    文档和更完整的示例在下面提供

    翻译自: https://medium.com/@theangroid/just-add-mvi-with-orbit-2-79ea66e3136

    相关资源:论文研究-基于MVI56-MNETR的Modbus TCP/IP通讯 .pdf
    Processed: 0.031, SQL: 8