当前位置:网站首页>MVVM has become history, and Google has fully turned to MVI
MVVM has become history, and Google has fully turned to MVI
2022-06-23 05:07:00 【Be a happy yard farmer】
Preface
I wrote some introductions some time ago MVI Architecture article , But there is no best architecture for software development , Only the most appropriate architecture , At the same time, it is well known that ,Google What is recommended is MVVM framework . I believe many people will have questions , Why don't I use the official recommendation MVVM, And use what you said MVI Architecture? ?
But I check these days Android Application Architecture Guide , Discover that the best practices recommended by Google have become One way data flow + Centralized state management , Isn't that what MVI Architecture ? look Google Has started to recommend MVI Architecture , It is also necessary for us to start to understand Android The latest version of the Application Architecture Guide ~
Overall framework
Two architectural principles
Android There are two main principles for the architecture design of
Separation of concerns
The most important principle to follow is to separate concerns . A common mistake is in a Activity or Fragment Write all the code in . These interface based classes should contain only the logic that handles interface and operating system interactions . In general ,Activity or Fragment The code in should be as lean as possible , Try to migrate the business logic to other layers
Through the data-driven interface
Another important principle is that you should use a data-driven interface ( The best is the persistence model ). The data model is independent of the interface elements and other components in the application .
This means that they are not related to the life cycle of the interface and application components , But it will still be destroyed when the operating system decides to remove the application's process from memory .
Data model and interface elements , Life cycle decoupling , Therefore, it is convenient to reuse , At the same time, it is easy to test , More stable and reliable .
Recommended application architecture
Based on the common architectural principles mentioned in the previous section , Each application should have at least two layers :
- Interface layer - Display application data on the screen .
- The data layer - Provide the required application data .
You can add an additional one named “ Domain layer ” Architecture layer of , To simplify and reuse the interaction between the interface layer and the data layer
As shown above , The dependencies between the layers are unidirectional , Domain layer , The data layer does not depend on the interface layer
Interface layer
The function of the interface is to display application data on the screen , And respond to user clicks . Whenever the data changes , Whether it's because of user interaction ( For example, pressing a button ), Or because of external input ( For example, network response ), The interface should be updated , To reflect these changes .
however , The format of application data obtained from the data layer is usually different from UI Format of data to be displayed , So we need to transform the data layer data into the state of the page
Therefore, the interface layer is generally divided into two parts , namely UI Layer and State Holder,State Holder The role of is usually played by ViewModel To undertake
The role of the data layer is to store and manage application data , And provide access to application data , Therefore, the interface layer must perform the following steps :
- Get application data , And convert it to
UICan be easily presentedUI State. - subscribe
UI State, Refresh when the page state changesUI - Receive user input events , And handle it according to the corresponding events , To refresh
UI State - Repeat paragraph as necessary 1-3 Step .
It is mainly a one-way data flow , As shown in the figure below :
Therefore, the interface layer mainly needs to do the following work :
- How to define
UI State. - How to use one-way data flow (
UDF), As providing and managingUI StateThe way . - How to expose and update
UI State - How to subscribe
UI State
How to define UI State
If we want to implement a news list interface , How do we define UI State Well ? We encapsulate all the states required by the interface in one data class in .
And previous MVVM One of the main differences between patterns is also here , It is usually a State Corresponding to one LiveData, and MVI Architecture emphasizes UI State Centralized management of
data class NewsUiState(
val isSignedIn: Boolean = false,
val isPremium: Boolean = false,
val newsItems: List<NewsItemUiState> = listOf(),
val userMessages: List<Message> = listOf()
)
data class NewsItemUiState(
val title: String,
val body: String,
val bookmarked: Boolean = false,
...
) In the example above UI State The definition is immutable . The main advantage of this is , Immutable objects ensure that the state of the application is immediately available . thus ,UI Can focus on a single role : Read UI State And update its... Accordingly UI Elements . therefore , Do not directly in UI Revision in China UI State. Violation of this principle will lead to multiple trusted sources of the same information , This leads to the problem of inconsistent data .
for example , From the above UI State Of NewsItemUiState Object bookmarked Mark in Activity Class has been updated , Then the tag will compete with the data tier , This leads to the problem of multiple data sources .
UI State Advantages and disadvantages of centralized management
stay MVVM We usually have multiple data streams , That is, a State Corresponding to one LiveData, and MVI Is a single data stream . What are the advantages and disadvantages of each ?
The main advantage of a single data stream is convenience , Reduce template code , To add a state, just give data class Add an attribute , Can effectively reduce ViewModel And View The cost of communication
meanwhile UI State Centralized management can easily achieve something like MediatorLiveData The effect of , For example, it may only be when the user is logged in and is a subscriber of a paid news service , You need to display the bookmark button . You can define UI State:
data class NewsUiState(
val isSignedIn: Boolean = false,
val isPremium: Boolean = false,
val newsItems: List<NewsItemUiState> = listOf()
){
val canBookmarkNews: Boolean get() = isSignedIn && isPremium
} As shown above , The visibility of bookmarks is a derivative of the other two properties , When the other two properties change ,canBookmarkNews It will also change automatically , When we need to implement the visible and hidden logic of Bookmarks , Just subscribe canBookmarkNews that will do , This makes it easy to achieve something like MediatorLiveData The effect of , But far more than MediatorLiveData Be simple
Of course ,UI State There are also some problems with centralized management :
- Unrelated data types :
UISome of the required states may be completely independent of each other . In such cases , The cost of tying these different states together may outweigh their advantages , Especially when the update frequency of one state is higher than that of other States . UiState diffing:UiStateThe more fields in the object , The more likely the data flow is to be sent out because one of the fields is updated . Because the view does notdiffingMechanism to know whether the data streams sent out continuously are the same , So each issue will cause the view to update . Of course , We canLiveDataorFlowUsedistinctUntilChanged()And so on , To solve this problem
Use one-way data flow management UI State
Mentioned above , In order to ensure UI Status cannot be modified in ,UI State The elements in are immutable , So how to update UI State Well ?
We usually use ViewModel As UI State The container of , So update in response to user input UI State Mainly divided into the following steps :
ViewModelWill store and publishUI State.UI StateIs aViewModelConverted application data .UILayer orientationViewModelSend user event notification .ViewModelWill handle user actions and updateUI State.- The updated status will be fed back to
UITo render . - The system will repeat the above operation for all events that cause the state change .
for instance , If the user needs to bookmark the news list , Then you need to pass the event to ViewModel, then ViewModel to update UI State( There may be data layer updates in the middle ),UI Layer subscription UI State Subscription response refresh , To complete the page refresh , As shown in the figure below :
Why use one-way data flow ?
One way data flow can implement the separation of concerns principle , It can put the state change source location 、 The conversion position and the final use position are separated .
This separation allows UI Only play the role indicated by its name : Through observation UI State Change to display page information , And pass user input to ViewModel To achieve status refresh .
let me put it another way , One way data flow helps achieve the following :
- Data consistency . The interface has only one trusted source .
- ...Testability . The source of state is independent , Therefore, it can be tested independently of the interface .
- Maintainability . The change of state follows a well-defined pattern , That is, state change is the result of the joint action of user events and their data pull sources .
Exposure and update UI State
Well defined UI State And determine how to manage the corresponding status , The next step is to send the provided status to the interface . We can use LiveData perhaps StateFlow take UI State Convert to a data stream and expose to UI layer
In order to ensure that you cannot UI Modification status in , We should define a variable StateFlow With an immutable StateFlow, As shown below :
class NewsViewModel(...) : ViewModel() {
private val _uiState = MutableStateFlow(NewsUiState())
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
...
} thus ,UI The layer can subscribe to status , and ViewModel You can also modify the status , Take the case where an asynchronous operation is required , have access to viewModelScope Start the coroutines , And the status can be updated when the operation is completed .
class NewsViewModel(
private val repository: NewsRepository,
...
) : ViewModel() {
private val _uiState = MutableStateFlow(NewsUiState())
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
private var fetchJob: Job? = null
fun fetchArticles(category: String) {
fetchJob?.cancel()
fetchJob = viewModelScope.launch {
try {
val newsItems = repository.newsItemsForCategory(category)
_uiState.update {
it.copy(newsItems = newsItems)
}
} catch (ioe: IOException) {
// Handle the error and notify the notify the UI when appropriate.
_uiState.update {
val messages = getMessagesFromThrowable(ioe)
it.copy(userMessages = messages)
}
}
}
}
} In the example above ,NewsViewModel Class will try to make a network request , And then update UI State, then UI Layers can react appropriately to it
subscribe UI State
subscribe UI State It's simple , Only need UI Layer view and refresh UI that will do
class NewsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Update UI elements
}
}
}
}
}UI State Local refresh
because MVI Under the framework of UI State Centralized management of , So updating a property will result in UI State Update , So how to implement local refresh in this case ?
We can use distinctUntilChanged Realization ,distinctUntilChanged The refresh will only be called back after the value has changed , This is equivalent to an anti shake property , So we can implement local refresh , Use as follows
class NewsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Bind the visibility of the progressBar to the state
// of isFetchingArticles.
viewModel.uiState
.map { it.isFetchingArticles }
.distinctUntilChanged()
.collect { progressBar.isVisible = it }
}
}
}
} Of course, we can also encapsulate it , to Flow perhaps LiveData Add an extension function , Make it support listening properties , Use as follows
class MainActivity : AppCompatActivity() {
private fun initViewModel() {
viewModel.viewStates.run {
// monitor newsList
observeState([email protected], MainViewState::newsList) {
newsRvAdapter.submitList(it)
}
// Monitor network status
observeState([email protected], MainViewState::fetchStatus) {
//..
}
}
}
} About MVI The architecture supports attribute listening , More detailed content is visible :MVI Architecture better practices : Support LiveData Property listening
Domain layer
The domain layer is an optional layer between the interface layer and the data layer .
The domain layer is responsible for encapsulating complex business logic , Or by multiple ViewModel Reusable simple business logic . This layer is optional , Because not all applications have such requirements . therefore , You should use this layer only when you need it .
The domain layer has the following advantages :
- Avoid code duplication .
- Improve readability of classes that use domain layer classes .
- Improve application testability .
- Enables you to divide responsibilities , To avoid large classes .
I feel that for the common APP, Domain layer doesn't seem necessary , about ViewModel The logic of repetition , Use util Generally speaking, it is enough
Maybe the domain layer is suitable for very large projects , You can choose according to your own needs , For more information about the domain layer, see :https://developer.android.com/jetpack/guide/domain-layer
The data layer
The data layer is mainly responsible for the logic of acquiring and processing data , The data layer consists of multiple layers Repository form , Each of them Repository Can contain zero to more Data Source. You should create a for each different type of data that the application processes Repository class . for example , You can create... For movie related data MoviesRepository class , Or create... For payment related data PaymentsRepository class . Of course, for convenience , For those with only one data source Repository, You can also write the data source code in the Repository, When there are multiple data sources in the future, split them
The data layer is the same as before MVVM The data layer under the architecture makes no difference , I won't go into that , Detailed information about the data layer can be found in :https://developer.android.com/jetpack/guide/data-layer
summary
Compared with the Old Architecture Guide , The new version mainly adds the domain layer and modifies the interface layer , The domain layer is optional , Each of you should use... According to your own project needs .
The interface layer starts from MVVM Architecture becomes MVI framework , Emphasized the importance of data One way data flow And Centralized management of status . comparison MVVM framework ,MVI The architecture has the following advantages
- Emphasize the one-way flow of data , It's easy to track and trace state changes , In data consistency , ...Testability , Maintainability has certain advantages
- Emphasize on
UI StateCentralized management of , Just subscribe to oneViewStateYou can get all the status of the page , relativeMVVMReduced a lot of template code - To add a state, you only need to add an attribute , To reduce the
ViewModelAndViewCommunication cost of layer , Focus business logic onViewModelin ,ViewThe layer only needs to subscribe to the status and then refresh
Of course, there is no best architecture in software development , Only the most appropriate architecture , You can choose the appropriate architecture for the project according to the situation , Actually in my opinion Google Recommended in the guide MVI Instead of MVVM, Probably for the sake of unification Android And Compose The architecture of . Because in Compose There is no bidirectional data binding in , Only one-way data flow , therefore MVI Is the best fit for Compose The architecture of .
Of course, if your project doesn't use DataBinding, Maybe you can start to try MVI, Don't use DataBinding Of MVVM The schema is switched to MVI The cost is not high , Switching is also relatively simple , In the ease of use , Data consistency , ...Testability , Maintainability and other aspects have certain advantages , You can also seamlessly switch to Compose.
边栏推荐
- [graph theory] - bipartite graph
- Parameter passing of 18 generator function
- Is data scientist a promising profession?
- Notepad++ find replace group replace retain
- TabControl style of WPF basic control
- Composer by installation laravel
- Thinkphp6 linked table query without associated ID (2 tables) is mainly the application of select
- Current relay hdl-a/1-110vdc-1
- Do280openshift command and troubleshooting -- common troubleshooting and chapter experiments
- Abnova acid phosphatase (wheat germ) instructions
猜你喜欢

Static two position relay xjls-84/440/dc220v

dolphinscheduler 1.2.1 数据迁移到 dolphinscheduler 2.0.5方法及迁移后数据测试记录

Apache atlas quick start

聊聊 C# 中的 Composite 模式

Abnova fluorescent dye 555-c3 maleimide scheme

微信小程序:星际旅行飞船乘坐票制作生成

微信小程序:凑单满减计算神器

Thesis reading_ Relation extraction_ CASREL

Go learning record II (window)

How to make social media the driving force of cross-border e-commerce? This independent station tool cannot be missed!
随机推荐
Thinkphp6 solving jump problems
396. mine site construction
DO280OpenShift命令及故障排查--常见故障排除和章节实验
STL tutorial 3- exception mechanism
Left and right values
ApiPost接口测试的用法之------Get
An understanding of free() (an error in C Primer Plus)
Welcome to the CSDN markdown editor
Flask Foundation: environment setup + configuration + mapping between URL and attempt + redirection + database connection
Notepad++ find replace group replace retain
ICer技能02makefile脚本自跑vcs仿真
Abnova blood total nucleic acid purification kit protocol
Talk about the composite pattern in C #
rtklib2.4.3 b34的一个与编译器有关的bug
数据科学家是不是特有前途的职业?
微信小程序:拼图工具箱
"Wechat applet - Basics" takes you to understand the routing system of the applet (2)
【图论】—— 二分图
轮播图的实现
centos7部署docker,安装mysql