ViewModel framework was introduced as part of Android Architecture Components – a set of experimental features for Android that were announced by Google at Google IO 2017 conference.
On the surface, the purpose of ViewModel looks very legitimate – it can be used in order to associate some data with the logical scope of either Activity or Fragment. It means that ViewModel itself and, consequently, the objects referenced by it will be preserved upon configuration change.
The stated advantage of such an approach is that it will reduce the burden placed on Android developers by Activity and Fragment lifecycle.
Sure thing we all need this feature! Like, yesterday!
Comparison of ViewModel to the “old” approach:
But let’s take a step back for a moment.
How did we retain data in e.g. Activity upon configuration change until now? The following code snippet demonstrates the “old” approach:
public class OldApproachActivity extends Activity { private static final String USER_ID = "USER_ID"; private static final String PRODUCT_ID = "PRODUCT_ID"; private String mUserId; private String mProductId; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { mUserId = savedInstanceState.getString(USER_ID); mProductId = savedInstanceState.getString(PRODUCT_ID); } ... } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(USER_ID, mUserId); outState.putString(PRODUCT_ID, mProductId); } ... }
For comparison, retaining the same variables during configuration change using ViewModel could be done like this:
public class ViewModelActivity extends Activity { private MyViewModel mViewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mViewModel = ViewModelProviders.of(this).get(MyViewModel.class); ... } ... public static class MyViewModel extends ViewModel { private String mUserId; private String mProductId; } }
If I would need to decide which approach is better based on the simple samples above, it is only natural to choose the one involving ViewModel. It is shorter and clearer.
The no-so-evident gotcha, however, is that the above two code snippets are not functionally equivalent!
During “normal” interaction of the user with the application both approaches produce the same result, but the “old” approach also supports “save and restore” flow, whereas the one involving ViewModel doesn’t.
Save and Restore Flow:
As I explained in the article about Android memory management and save & restore, Android kills applications when the operating system runs low on memory.
In most cases, Android will notify the process that it is about to be killed, thus giving it a chance to save application’s state. Then, when the application is started again (either due to user interaction or automatically), it will be given an opportunity to restore its previous state. This flow is called “save and restore”.
Let’s review one specific example.
Suppose that the application has a back stack of Activities
when Android decides to kill it.
Before killing the application, Android will call onSaveInstanceState(Bundle)
method of each Activity
on the back stack. Developers need to use the provided Bundle
in order to persist data that will allow to restore the state of each Activity
in the future.
In addition, Android will automatically save the state of the back stack itself (which Activities
are shown and their order).
When the application is restarted, the system will automatically restore the previous state of the back stack. User can then navigate back to all Activities
that had been present on the back stack when the application was killed, and Android will automatically recreate them.
When Activities
from the back stack are being recreated, Android passes the respective saved data to onCreate(Bundle)
methods. This saved state data can be used by developers in order to restore the state of each Activity
.
The process of save and restore of each Activity
can be roughly summarized as follows:
Correct implementation of “save and restore” flow allows the user to have a fluent experience that is not affected by Android’s memory management activities.
If you go back and read the code demonstrating the “old” approach above, you will notice that in addition to supporting configuration changes, it also supports save and restore flow. In case of the new approach involving ViewModel
, however, save and restore flow results in loss of developer managed state.
Note that only developer managed state will be lost. Android will always do its part and restore the state it is responsible for. It is only the state kept in ViewModel
that will not be restored.
This state inconsistency is much worse than not restoring any state at all. Imagine that the user can navigate back to previous Activities
, but their state was restored only partially (e.g. some references that must not be nullable during normal operation are nulls).
In such a case, you will be lucky if application just crashes with NPE. Then, at least, the user will know that something bad happened and have a chance to start application from scratch.
Much worse if the application does not crash, but the user is stuck with e.g. infinite progress indication or inconsistent UI.
The worst of all scenarios is if the application seems to be working, but silently corrupts user’s data.
The video below demonstrates a bug related to save and restore flow that I found in the new StackOverflow application for Android:
The bug demonstrated in this video is the least dangerous of all save & restore bugs – just user interface inconsistency, followed by application’s crash. Imagine how frustrated it would be if I would write a detailed answer and only then the application would crash and loose all this content.
Why Care About Save and Restore Flow:
Some developers argue that save and restore is such a rare scenario, that it can be ignored until proven otherwise.
Other developers don’t go as far as ignoring save and restore, but still say that if it occurs then it means that the user haven’t interacted with the application for a long time. In this case, they say, we can just assume that previous state is outdated anyway and restart the application from scratch.
You might’ve heard additional forms of this argument. The assumption underlying all these claims is that save and restore does not occur very often.
The reality, however, is that save and restore flow is being constantly experienced by majority of your users.
I experience save and restore on a daily basis on my personal Samsung Galaxy S4 having 2GB of RAM.
Take a look at this chart from AppBrain showing the most used Android phones as of May 2017:
Note how your average user is more likely to have Samsung Galaxy S3 than Samsung Galaxy S7 Edge. Also note that the second most used phone is Samsung Galaxy Grand Prime which has just 1GB of RAM!
Given that Samsung phones are on the high performance side, users of other phones are most likely to have similar or even smaller amounts of RAM.
Think about it – a huge amount of your users (probably above 50%, unless you have a niche market) own phones that have less than 2GB of RAM. [Edit: I got this estimation completely right; read the article about Android memory management and save & restore for a much more detailed market overview]
On my Samsung Galaxy S4, which has 2GB of RAM, Android starts killing applications after I have approximately 10 applications open in the background. Your average user will have more than 10 applications in his/her “recents” list and will experience save & restore on a daily basis too.
Therefore, handling of save & restore is not optional. Professional Android developers who care about their users must implement a first class support for save and restore in their applications.
Equipped with this knowledge let’s get back to ViewModel architecture component.
Usefulness of ViewModel:
In the previous section I explained why support for save & restore flow is not optional and must always be implemented. In addition, earlier in this article you saw that implementation of save and restore also handles configuration changes.
So, why would you want to use ViewModel
then? I mean, if you must implement support for save & restore anyway, and this functionality also handles configuration changes, why invest additional effort into ViewModel?
Note that this will always be additional effort because even if you use ViewModel, you will still need to implement the logic that handles save & restore.
There is one use case for ViewModel
that was stated by many developers. Since it seems to be so resonating with the community, let’s discuss it in details.
Turns out that there is an upper limit on the amount of data that can be stored during save and restore flow. I couldn’t find the exact number, but looks like it is at approximately 500kB.
If you attempt to save more than 500kB of data during onSaveInstanceState()
call, you will be presented with TransactionTooLargeException
.
In this situation it becomes very tempting to use ViewModel
in order to retain big data sets during configuration changes.
In my opinion, under no circumstances should large data sets be “stored” in either Activities
or Fragments
. Let me explain why using two imaginary scenarios.
Imagine that the user interacts with Activity
and fetches 1MB of data from the web. If all this data is stored in Activity
then when the user clicks “back” button and navigates to the previous screen this data is lost. If at this point the user decides to open the same Activity
again, then all the data will need to be re-fetched from the network. This network call duplication due to a simple back-and-forth between screens is not a very good design.
Now imagine that we implement search functionality. In this case, even if the user searches for the same string, we must still fetch a new data from the network upon each search request. We are not concerned with back-and-forth between screens anymore because we hit the network anyway. It might seem that storing search results in Activity
is a valid design choice in this case, but imagine that you discover at some point that you need the search results in other part of the application. The fact that search results are stored in Activity
becomes an obstacle because there is no reliable way to access them outside of this Activity
.
The above examples show that while ViewModel
can be used in order to retain large data sets upon rotation, it is probably a bad design choice to start with. The fact that it can be done doesn’t imply that it should be done.
The only state that should be stored in Activities
and Fragments
is UI state and data identifiers.
Large data sets should be stored in Application
scope, or, depending on the requirements, persisted to SQLite or some other persistent storage.
Since there is no clear valid use case for ViewModel
Architecture Component, its introduction can be hardly justified.
ViewModel Architecture Component Considered Harmful:
In the previous section we discussed why ViewModel
is somewhat useless addition to developer’s toolbox. However, I think that it is not just useless, but harmful.
As I said earlier, many developers (especially inexperienced ones) don’t realize the importance of proper save & restore handling.
Furthermore, even when developers do implement the logic that handles save & restore, tests for this feature rarely included in the testing plan. [if you’re interested, I wrote an article that explains how to test save & restore properly]
While save and restore support is often neglected and rarely tested (in my experience; YMMV), configuration changes support is much more common. In fact, any application that is not locked to portrait mode will be thoroughly tested in context of screen orientation change.
Until today, the fact that support for configuration changes also provided support for most parts of save and restore flow mitigated the problem a bit. And even when bugs with save and restore were found, they could be fixed while keeping the general design intact.
Introduction of ViewModel
changes the situation cardinally. Now developers will be able to implement support for configuration changes while completely ignoring save and restore flow.
It doesn’t take too much imagination to see that the following two step scenario will take place on many projects:
- Application is released without save and restore support. Users are being bombarded by save and restore bugs.
- Upon realization of the problem, developers “hack” save and restore support alongside
ViewModel
approach.
The immediate result of lack of save and restore support will be user facing bugs. Some of them might be as innocent as NPE crashes, others might silently corrupt users data.
Assuming best case scenario of application crashes, developers will be able to quickly identify the issues using various “crashlytics” tools. If the application will just hung or silently corrupt user’s data, then developers will need to investigate the problem based on 1-star user reviews in Google Play.
Once the root cause of the bugs will be identified, save and restore support will need to be implemented. However, it is not likely that developers will refactor the applications in order to get rid of ViewModel
– at this point it will be too risky and time consuming. Therefore, save and restore support will be just hacked on top of the existing design. The result of these hacks will be long term maintainability issues and more bugs.
And there is more to it.
Since it is so simple to retain data in Activities
and Fragments
using ViewModel
approach, the average amount of memory consumed by applications will increase. Even if different components will need access to the same data, it will be very easy to have each of them to retain its own dataset.
Applications will consume more memory, which will lead to more frequent killing of processes by Android, which will lead to the bugs in save and restore to affect even more users.
Positive feedback of bugs…
In addition, ViewModel has its own lifecycle. Therefore, it will make the overall lifecycle of the application more complex.
The rule that ViewModel should not reference Activities is the manifestation of the aforementioned complication of lifecycle, which developers need to understand and handle.
Summary:
There has already been an attempt by Google to make the handling of configuration changes easier by preserving data – Loaders. Yes, Loaders handled concurrency (in a terrible way), but the main motivation behind LoaderManager framework was to preserve data upon configuration changes.
Loaders consumed a lot of community’s attention and effort until we learned their limitations and costs. I myself spent a considerable time learning about Loaders, implementing them and then refactoring out of my codebase when I realized how much they complicate the design.
I believe that ViewModel will eventually share the fate of Loaders. It is not that horribly complicated, but its existence is completely unjustified IMHO.
In addition, this “architecture component” has nothing to do with MVVM architectural pattern.
ViewModel class can be used in implementation of MVC and MVP. Hell, it can be used in the “classical Android approach” when all the logic resides in a single Activity.
Naming is important in software, and, in my opinion, the fact that this library was so misleadingly named speaks volumes. A library that was intended to be used by millions of developers who build applications for billions of users should have been held to a higher standards.
That’s all I wanted to say about ViewModel.
Please leave your comments and questions below, and consider subscribing to our newsletter if you liked this post.
ViewModel is only useless if you use it to persist state of small objects. If you are holding very large objects in memory (anything 0.5 MB or greater), then it is to persist state across configuration changes as it helps avoid the TransactionTooLargeException.
An ideal implementation would be to save an identifier of large objects in instance state and use ViewModel to persist the object across rotations. In this manner, we get fast and cheap object persistence across configuration changes, but still have the ability to recover from process/activity death.
I would argue that TransactionTooLargeException is a hint that it is time to have a critical look at your design, rather than a sign that ViewModel is required.
Let’s say you use ViewModel like you said. If user navigates back and then opens the same activity again, the data will not be there. So, if ViewModel was the only mechanism you used in order to store 0.5MB of data – all this data will need to be re-fetched and re-processed just because the user made a simple back-and-forth navigation between screens. This design is not optimal and should be reviewed.
Hi Vasiliy,
I enjoyed reading your article and I think you make many valid points. I especially liked your description of the categories of bugs and how developers will deal with them (Crashlytics, 1-star reviews, etc.) — I think this is spot on.
The one thing this article, and in particular your response to Riley, seems to miss is that (IMHO) the user is much more likely to encounter a configuration change (specifically: device rotation) than a save & restore flow. Also, it is very easy for the user to make a configuration change (again: device rotation) inadvertently. I myself often rotate my phone into landscape mode by accident just to immediately straighten it back into portrait mode, whereas leaving an application or activity by accident happens less frequently.
Because of this, I think it is reasonable to ask whether we should handle configuration change and save & restore differently. And by “differently” I mean “faster” — I would like to keep in memory the data set necessary to restore the activity/fragment after an orientation change, regardless of how large that data set is.
I am curious what you think is the optimal design to achieve this.
Hey Zsombor,
It’s really broad question and the general answer is: “it depends”. However, let me point to several important concepts:
I was involved in development of a decent number of apps, but I almost never needed the functionality provided by ViewModel. And whenever I did need to preserve either lots of data or transient state over config change, I usually simply moved that piece of logic into Application scope. I think it’s better and much simpler design than hacking around with ViewModel.
So, these are my general thoughts.
This comment is a bit late in the lifecycle of this article, but one thing I really don’t see covered in your article is: what if you have an app that has many (i.e., potentially dozens) different Fragments and/or Activities that all have some form of data set associated with them and ONLY them? Or perhaps, you may have Fragments that store the same data structurally, but they need their own instances of the actual data because they need to be different (ex: maybe you have an Activity that has two of the same Fragment that has product info/specs that is used to compare them side-by-side: each one would have the same structure but entirely different data). Whatever the case may be, my point is: at some point would it not become much too complicated to manage dozens of unique datasets at the Application level? Not to mention it’s possible none of these are shared, meaning you will never have the described scenario of loading data once then accessing the same data later in a different place. ViewModel allows very easy management of all of this data that essentially “remembers” all of these things in an organized way at the Application level already. It also does make it very easy for Fragments in the same Activity scope to share data because they can simply request the Activity-specific ViewModel rather than their own Fragment-specific model.
I also agree with some other commenters that ViewModel and the save-restore flow should be handled separately. As you mentioned, the “old” system was only better in terms of save-restore functionality by coincidence. Proper practice should be to implement it properly regardless of which method you manage your data. On top of that I don’t really feel like it’s a “hack” to implement save-restore flow on top of ViewModels. It’s very easy to grab the data from a ViewModel in onPause() or onSaveInstanceState() and persist it somehow (saved state bundles, for example), then when your Activity or Fragment is re-created, simply check to see if you have a “brand new” ViewModel and if so, pass the saved data to some sort of restoreState(data…) method in your ViewModel.
Another thing ViewModel makes very easy is separation of data loading and management from the UI thread due to LiveData and the Observer pattern. The ViewModel can do all of its network or database accesses on a background thread then update the stored LiveData, which will then notify the Observer on the UI thread so the Activity/Fragment can simply get the new data and bind it to its Views. As far as I know this isn’t a required way to use ViewModels, but it’s recommended in the Android Developer Guide so I imagine it’s very commonly done.
Hello Cale,
Thanks for your comment.
Sure, there might be scenarios where ViewModel could be beneficial, but the one you described is so artificial that I don’t think it’s any common. The main point to remember in respect to ViewModel is that it didn’t bring anything new – you could always preserve data on config changes in multitude of ways (of which Application scope is just one example). Therefore, the best you can say about ViewModel is that it makes it a bit easier to handle some quite rare requirements. However, as I wrote in this article, the downsides are much bigger.
In addition, ViewModel doesn’t make it easier to separate UI layer from other logical layers. It’s just a simple object retained across config changes. Whatever separation you can achieve with ViewModel, you can also achieve without it.
If user is in the same app and navigates back to an activity which was in back stack then ViewModel will provide the current/latest data. That does not mean it re-fetched or re-processed. As ViewModel is lifecycle aware it will be destroyed with the destruction of the activity it is attached to. Then I think it is justified because when the activity is recreated from scratch it will get the data from newly created ViewModel as well.
And the point you made about save and restore flow, also shown in the video, really questions why we need ViewModels if we still need to save the UI states with it. But I think ViewModel gives us another advantage of using LiveData with it. By using LiveData we make our code observable so it kinda updates itself and we need to write less code of updating the UI.
One other benefit of ViewModels not outlined here is transient/non-config instances, e.g. Threads or network requests.
I agree that using ViewModels might cause devs to overlook Config saving, and that is something that needs to be more explicitly addressed in the Arch Components. However, ViewModels in this paradigm allows for better code fluidity between Activity instances. VMs allow holding on to necessary & long-lived transient objects without hot-swapping callbacks or incorporating a temp cache strategy to handle config-changes appropriately.
Hi Tony, thanks for your comment.
You’re right that ViewModel can allow for relatively easy “sharing” of e.g. network requests between instances of the same Activity on config changes. However, in order to do this ViewModel will need to have a reference to these network requests.
This might not be evident immediately, but if you try to implement this approach in code you will notice that parts of business logic start leaking into ViewModel. The more such parts you’ll have, the more you will contaminate the ViewModel with logic.
Such a contamination is not in line with the main purpose of MVVM: separation between business and presentation logic. It is, in essence, just a quick and dirty hack that will have negative impact on maintainability of your project. Therefore, while what you say can be done, I doubt that it should be done.
What’s your alternatives? From the top of my head:
1) Encapsulate network requests in classes that “live” in Application scope.
2) Encapsulate network requests in Services
3) Encapsulate network requests in Sync Adapter, JobScheduler, etc.
All of these alternatives require more work, but this is a classical engineer’s dilemma: do it quick and dirty, or do it right. In my experience, quick and dirty is never a good idea.
Hi,
I do not understand the comparison between ViewModel and the “Save and Restore flow”. Simply put, IMHO these facilities are meant to be used in different contexts. Before ViewModel was there an easy way to preserve state on configuration changes? It was a nightmare with retained fragments for example. It was more easy to forget to handle some lifecycle flows, propagate data between fragments and activities and so on. Not to mention other approaches… In any case propagating state between activities instances using OnSaveInstanceState was and, as you said, is necessary. I think that the ViewModel covers a huge missing part of the Android platform (not to mention the possibility to be able to observe data, share live data between fragments without worrying in almost all cases to register/deregister listeners and so on). Surely it is perfectible as everything. How would you improve/replace it?
Thanks.
Hi, thanks for your comment.
You are right – ViewModel and save and restore flow are different contexts. This is exactly the major deficiency of ViewModel that I tried to show in this post.
If you think about it conceptually, then save and restore and config changes require the same functionality – preserve data upon Activity (or Fragment) being destroyed and re-created.
It doesn’t make sense to use two completely different approaches in order to achieve the same functionality. If you do use both (and I invite you to try to do it in a real application, not artificial examples), you will notice that it involves more effort and results in more complex design. It also gives rise to corner case scenarios related to integration between these approaches, which means that there will be more bugs.
If you do use both, you’ll have a very bad design, but, at least, be covering both bases. My main problem with ViewModel is that it makes it very easy to forget about save and restore completely and handle only config changes. You might be an experienced developer who knows how to implement and test save and restore support, but many developers don’t. This means that users will get more save and restore bugs.
For example of save and restore bug in StackOverflow (!) application see this video: https://youtu.be/-GAoc0hJ4_I
> . Before ViewModel was there an easy way to preserve state on configuration changes?
Yes, you could override `onRetainCustomNonConfigurationInstance()`, and whatever you returned as an Object was retained across config changes for you.
It has since been deprecated because the Jetpack team doesn’t like how simple it is (probably).
I agree with you Vasiliy.
Today I tried for the first time the codelab on ViewModel and, since I’m an experienced developer, the first thing I asked myself was: let’s try to enable the “Don’t keep activity” flag and see if it works. And the result was quite disappointing to me…
What is the difference between ViewModel and standard Singleton class that can hold the data until you decide to clear it and will be wiped on App killed by the OS?
You can think of ViewModel as a “mini-Singleton” which lives in Activity/Fragment logical scope and will be wiped when the respective logical scope ceases to exists.
It’s completely wrong statement.
ViewModel has nothing with Singleton, because system controls Lifecycle of ViewModel fragment. Otherwise any activity/fragment would considered as this mythic “mini Singleton”.
Hello Andrei,
I guess it is a matter of interpretation.
ViewModels are very much like Singletons because you obtain a reference to them using static method calls, and, as long as logical scope is not destroyed, all these calls will result in the reference to the same object.
To my best knowledge, Activities/Fragments are not being cached and retrieved through static method calls, therefore, I would say, you probably misunderstood the argument.
It’s not like Singleton, because you don’t have any static reference to ViewModel, and even don’t have any app level reference. ViewModel.get is just provider that creates a new instance or returned cached instance from Fragment manager.
So it’s just incorrect to try consider VM as any kind of Singleton, because it doesn’t work as Singleton but on application level + temporary, application aware cashing
And Fragments of course cached by fragment manager and VM uses the same API.
Activities also cached, system keeps your activities alive in task stack, but can kill them at any moment, but practically does that not so often for modern Android devices
Of course, ViewModel doesn’t persist anything with don’t keep activities mode.
But from other point of view, system never manage activities like don’t keep activities, it’s useful but synthetic tool, that can show problems of your app with state, but VM never considered as replacement for data persistency or caching
ViewModel is a term from programming architecture to seperate view from model it is NOT a way to replace the onSaveInstanceState that should still be used to save the data (in the ViewModel off course as no IO operations should be handled in an activity or fragment). This article do more damage than good for new programmers to understand the fundamentals of programming architecture.
I advise you to read some literature on the MVVM architecture and revise this article after that.
Hello Andreas,
Thanks for joining the discussion.
I do agree that “view model” is a term related to presentation layer architecture. However, ViewModel architecture component is not directly related to MVVM in any way and can be used with any other approach as well (MVP, MVC, “all code in Activity”, etc.). I touched on this subject in this talk and you can watch it to get a more complete picture.
We can all agree (hopefully) that proper naming is important. Therefore, this is yet another major issue with ViewModel – it is a misleadingly named library.
So, I will take part of your advice.
I don’t think that I really need to read any more literature on MVVM. See, I’ve been publishing very comprehensive articles about MVx architectures in Android for years. Therefore, I believe that I have a very deep understanding of presentation layer architectures.
However, I will revise this article and mention that ViewModel architecture component is misleadingly named and is not related to MVVM in any way.
You state that the Android user should choose between onSaveInstanceState and ViewModel that is simply not a valid statement.
Andreas, I think you misunderstood the article a bit.
What I said is that Android developers must implement support for save&restore regardless of whether they use ViewModel or not. Therefore, there is no “choice between onSaveInstanceState and ViewModel”.
The only choice is to use ViewModel in addition to onSaveInstanceState or not. This article lists reasons to avoid it.
If you read the docs, ViewModel does not claim to take the place of save state. The docs actually explain how to use them together.
ViewModel alone is not an attempt to replace saveInstanceState
https://developer.android.com/topic/libraries/architecture/saving-states.html
Hello John,
Yes, I read the docs. In fact, I think this specific document might have been added as a result of my effort in surfacing the issues.
However, even though docs were expanded, documentation alone can’t resolve the fundamental issues discussed in this article.
In any case, this article don’t argue that ViewModel is a replacement of saveInstanceState. On contrary. You can read my responses to Andreas in the previous comments – I think you misunderstand the article similarly to the way he did.
Let me know if you have further questions.
Hi Vasiliy
Great article
I just implemented VM and was left wondering why bother as I still have to save and restore state…
Hi Simon,
Thanks for sharing your experience – it is an amazing one-liner that summarizes this entire article.
Great article ! I agree with most of your opinions :
– “ViewModel” is wrongly named, this component is more a data holder aware of the activity/fragment lifecycle it is bound to. And I agree that naming is very important when you design a library (and when you are Google). Finally it is harmful for the Android Databinding library, which can fully use ViewModels in an MVVM approach (BUT “ViewModel” Android component and Observable class from Android databinding cannot be painlessly used together -sick-)
– Even though it is wrongly named, the fact that the lifecycle is automagically managed made me try this component on a new project. What a disapointment when, after an hour of driving with Waze, I switched back to the in-development app, and saw that the ViewModel had been destroyed with the activity, but a contextful fragment had been restored by the system (context having been lost, of course) ! It let me think that this component is useless and dangerous, and finding your article comfort me in my opinion
And I don’t have a great opinion of the others architecture components too… :
– LiveData : Ok great, but why rewriting what RxJava already does ?? On Angular Google did not make the mistake of trying to rewrite RxJs, they just added a dependency to RxJs and focused on where they can bring the most value…
– Lifecycle observers : Fancy, but the number of use cases is rather limited IMHO
– Room: Requery does the same thing (and manages relationships…)
In the end, I think that these Android Components are at a draft state, and that other libraries I cited above should be prefered…
Julien,
Thanks for sharing your opinion and experience.
Great article, and I agree with Julien about the naming, its simply misleading, ViewModel from traditional MVVM architectures serves an entirely different purpose, the ViewModel from AAC is rather a Presenter which is not recreated on lifecycle events and that it doesn’t need a View interface reference from Views, if it acts like a Presenter then it should have been named as such.
Also you did a great job of explaining why apps should still handle onSaveInstanceState() etc, there’s even an article from Google developer on the same https://medium.com/google-developers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090
Hi Arif,
Thanks for sharing your opinion.
I think that ViewModel can be used as view model, presenter, controller, or just simply as Activity logical scoped object. Therefore, I don’t think that naming it Presenter would be much better. This functionality has nothing to do with organization of the presentation layer as far as I can tell.
Yeah, the article you linked is also a good read. You can even find my comment below it where we discussed the usefulness of ViewModel a bit more.
The fact that the ViewModel and LiveData are killed when an activity is destroyed (because of “do not keep activities”) is actually a good thing. It means the OS has determined it needs memory, and it’s taking it. So when that activity comes back to life, the data will not be available and you’ll have to recreate/re-fetch it again. In contrast, using singletons, as we do now in our MVP model, large chunks of data can stay in memory in our “presenter” objects, EVEN IF the activity and fragment are reclaimed/destroyed by the OS. This is probably NOT a good thing, as your memory footprint will continue to grow over time and the app will become bloated (fat in memory.) . Of course, one can have control over one’s cached singleton data I suppose, and get rid of it in onDestroy of the fragment or activity, however that sort of defeats the purpose of having it readily available so you don’t have to fetch it from the back end. I suppose another approach to singleton data is to have it timeout, and clear itself after it becomes stale. I was trying to create an MVVM demo for a whitespace project, but now I’m not that impressed, as it seems like we’re holding on to all the data we need simply by using application-scoped singletons as our presenters, which hold our data.
Is any of this a likely scenario in 2018, when we have super powerful Pixel phones?
Hi Igor,
I’m not sure what you ask. Can you elaborate a bit?
Well, you mentioned ‘weak’ devices in the beginning of your blog post, citing many users who still have these devices. I agree that on weak devices an application might get killed due to low memory. I postulate, however, that in 2018 nobody gives a crap about these weak devices, as everyone is trying to buy a Pixel phone or latest Samsung phone. What do you think?
The issue of save and restore is not resolved by stronger devices. It’s just mitigated a bit.
Let’s say that you have a device with 4GB of RAM that allows you to have ~20 applications in memory at every instant. The moment you try to open more apps than that, you’re back in the same situation. You might argue that not many users will use 20 apps on their devices (which is kind of true), but you wouldn’t like have the app crash for, say, 10% of your users, right?
In addition, your premise about everyone buying Pixel and Samsung holds only for niche markets. If your app targets western middle class and up, then it might be the case. However, globally, even devices with 1GB of RAM are still very common. I discussed this topic in more details in my article about Android memory management where I quoted some numbers from Q2 of 2017. I don’t think the picture changed that much since then.
Lastly, even though devices get more memory, applications also get more memory hungry.
Therefore, all in all, I would hesitate to make any simplifying assumptions regarding save and restore.
Thank you for clarifying that. Do you recommend using ViewModel POJO instead of tied to Android Activity/Fragment? That way we can use `getLastNonConfigurationInstance` without the risks you mentioned.
If you mean just defining a data structure and preserving it over config change using
getLastNonConfigurationInstance
calls then it’s totally alright. But don’t forget that you’d probably need to preserve it over save and restore as well, in which case why bother?One of the only cases that I know of when you’ll actually want to augment save and restore handling with special config change handling would be something like infinite scrollable list of search results with big data structures. Another case would be something like YouTube app where you want to preserve the state of the playback (and, potentially, even the UI).
However, these are exceptions. It’s alright to hack exceptions here and there. My main problem is that ViewModel is advertised as a general solution and the recommended approach, which is obviously a bad idea due to the reasons listed in this post and another ones (e.g. the fact that it’s absolute nightmare in terms of dependency injection).
It’s a very unfortunately designed API IMHO, similarly to AsyncTask and Loaders.
Very Nice article and valid points to consider.
I am trying to put my understanding here of VIewModel. Let me know what you think of it.
What i think of VIewModel is same that MVVM brings. Where ViewModel exposes observable that View Layer can observe to. VIewModel is kind of bridge between View Layer and Model classes (Model classes could be represented as Domain Layer or anything that does some logic and returns some value). Now for implementing save/restore, any way we have to implement it explicitly in Activity or Fragment.
Now just think by extending my `XYZViewModel` class with ViewModel (From arch components), out of the box, i have ability to use my same XYZViewModel instance to retain view state over config changes that allows me to skip restoration from bundle. We have something more in our tool box than before. Isn’t it great?
So i think its just matter of how you perceive a concept. And finally its up to developer to understand things the way they should work.
Interesting thoughts.
I could maybe agree, except that you don’t get ViewModel for “free”. You get it for additional complexity and inability to have transitive references to Context.
One of the consequent limitations is that you can’t control Fragments navigation from ViewModel. And, indeed, in most implementattions that I saw this logic is left for views to handle. That’s just one example, but there are more.
Hi Vasiliy, thank you for your article.
At present I am using a java class with public static setter and getter methods in my project. I was wondering if this is a right approach or should I switch to using ViewModel. I landed here while gathering more understanding on ViewModel. What is your opinion about using a normal java class with static public methods vs ViewModel. Thank you.
Hello Sandeep,
I’m not sure why you think that you need to choose between statics and ViewModel – I don’t see these as the only alternatives. I try to avoid static methods as much as possible and to avoid static state completely.
I recommend using dependency injection everywhere. Once you’re doing that, you can always make some objects global to the application and share any kind of state through them. You can also use persistence like shared prefs or databases to share state.
Hope this gives you some new ideas to explore.
Hi Vasiliy,
Thank you for your reply. Yes your reply has given me a direction to explore. I am new to Android development.
I will learn and try to implement dependency injection design pattern in my Android apps.
Thank you so much.
This sounds like par for the course from Google. I became sick of Android development not because of the evolving hardware or OS but because of the myriad counterproductive solutions in search of a problem that they cram down our throats and that become industry standards.
Everything from Fragments to Dagger to Data Binding has just been one hot mess after another and we are just stuck with them. Was once privy to a project that was started off without any of these tools that was very smooth and well architected. As stakeholders forced the team into adopting one after another of them the predictable and familiar pattern of unmaintainable nightmare fuel piled higher and higher.
The only exception to this rule I’ve seen was Android Studio and that’s only because of the amazing work of JetBrains.
Very insightful article. One hardly gets this kind of investigated articles on the internet. Everyone follow hype driven development. Keep up the good work.
Hi Vasiliy, what do you think about this library https://developer.android.com/jetpack/androidx/releases/lifecycle#viewmodel-savedstate-1.0.0-alpha01, It might solve the problem that you mention?
Hi Gabriel,
In my opinion, that’s exactly the hack that should’ve been developed in response to lack of preliminary architectural thinking. It’s ugly and I’m not sure it will improve the situation at all. Time will tell.
Interesting read, thank you Vasiliy!
(Complete noob in any native mobile development here.).
Having gone through Android Development 101 I have a few other such [very comfy looking] components with similar questions now: LiveData (any other lifecycle-aware observers?), navigation exclusively by NavHost around Fragments (aren’t those deadweight for all-the-rage deep-linking?), Room DAOs producing LiveData.
Would you recommend staying clear of those too?
Hello Sergey,
I’m not using any of these in my projects. It’s not an explicit recommendation to stay away of LiveData, but I really don’t think it’s the optimal approach for Android development.
Vasiliy
Hello Vasily,
Great article!! I read it a year ago and I read it again to remind me why ViewModels are not a real ViewModels. IMHO I think is just misnamed, should be named as DataHolder!! Even the ViewModel Saved State confirm what’s main responsibility of “ViewModel” is to hold data.
I think implementing the logic for handling save & restore for every screen requires lot of effort, which is often too expensive and therefore just not worth it.
It’s often okay to just reload the whole screen. Of course, you design it that way to reset the state properly to avoid crashes or illegal states. For me, this is the best effort considering the time required and value for the bussiness.
Sometimes, you might have large forms or similar functionality where persisting the state is important and you should put extra effort there to support save & restore too.
On most of the other screens, having a scope which survives configuration changes is just what you need. Fragments destroying their views, orinentation changes – this stuff happens all the time, and ViewModel makes it easy to handle that kind of stuff.
Overall, I would say ViewModel is a great tool which can be combined with SavedInstanceState where needed to fully support restoration.
Couldn’t agree more. Considering the fact that now there are two independent ways to save the state, i would say viewModel from google is completely useless and only causes more pain than the issues its trying to solve.
How about with new SavedStateHandle that fix this issue?
https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate
At this point in time, SavedStateHandle isn’t “new” anymore. Google had realized that they screwed up by not accounting for process death with ViewModel and rolled out another complex workaround. That’s exactly the “hack” that I predicted in this article, so the reality proved my analysis to be correct.
> you need to handle the case when network request completes after the previous Activity destroyed, but before new Activity created.
I’m pretty sure I heard somewhere official that ViewModel with LiveData handles this case by delaying the event until the new activity is created. Did they change it? Or were you talking about using it without LiveData? Or am I just misremembering
What I meant is that if you use ViewModel, you’ll need to make sure that state changes that took place during config changes are reflected in the new instance of Activity. LiveData is indeed one of the ways to support this requirement.