Android ViewModel Architecture Component Considered Harmful

//Android ViewModel Architecture Component Considered Harmful

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:

android_save_restore

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:

Most used Android phones

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:

  1. Application is released without save and restore support. Users are being bombarded by save and restore bugs.
  2. 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.

26 Comments

  1. Riley Ackerman May 30, 2017 at 2:48 pm - Reply

    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.

    • Vasiliy May 30, 2017 at 2:52 pm - Reply

      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.

  2. Tony May 30, 2017 at 9:12 pm - Reply

    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.

    • Vasiliy June 15, 2017 at 3:08 am - Reply

      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.

  3. Pa June 17, 2017 at 9:55 pm - Reply

    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.

    • Vasiliy June 18, 2017 at 11:58 am - Reply

      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

  4. Marco June 28, 2017 at 1:38 pm - Reply

    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…

  5. Galeen July 18, 2017 at 6:31 am - Reply

    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?

    • Vasiliy July 18, 2017 at 2:14 pm - Reply

      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.

      • Andrei Mishchenko February 6, 2018 at 12:49 am - Reply

        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”.

        • Vasiliy February 6, 2018 at 1:03 pm - Reply

          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.

          • Andrei Mishchenko February 6, 2018 at 3:59 pm

            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

          • Andrei Mishchenko February 6, 2018 at 4:02 pm

            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

  6. Andrei Mishchenko February 6, 2018 at 12:46 am - Reply

    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

  7. Andreas March 7, 2018 at 9:32 pm - Reply

    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.

    • Vasiliy March 9, 2018 at 7:54 am - Reply

      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.

      • Andreas March 9, 2018 at 9:53 pm - Reply

        You state that the Android user should choose between onSaveInstanceState and ViewModel that is simply not a valid statement.

        • Vasiliy March 10, 2018 at 12:23 am - Reply

          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.

  8. John Hiott March 19, 2018 at 7:59 pm - Reply

    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

    • Vasiliy March 19, 2018 at 8:13 pm - Reply

      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.

  9. simon April 8, 2018 at 8:45 am - Reply

    Hi Vasiliy
    Great article
    I just implemented VM and was left wondering why bother as I still have to save and restore state…

    • Vasiliy April 8, 2018 at 5:56 pm - Reply

      Hi Simon,
      Thanks for sharing your experience – it is an amazing one-liner that summarizes this entire article.

  10. Julien April 23, 2018 at 7:35 pm - Reply

    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…

    • Vasiliy April 23, 2018 at 7:41 pm - Reply

      Julien,
      Thanks for sharing your opinion and experience.

  11. Arif July 12, 2018 at 11:55 am - Reply

    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

    • Vasiliy July 12, 2018 at 12:01 pm - Reply

      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.

Leave A Comment

Stay Connected!

Receive notifications about new posts and resources straight into your inbox!
SUBSCRIBE
close-link