With the introduction of the Android Architecture Components library, several new classes were introduced, including AndroidViewModel and ViewModel. However, I'm having trouble figuring out the difference between these two classes. The documentation succinctly describes AndroidViewModel as follows:
Application context aware ViewModel
I appreciate the brevity, but what exactly does this imply? When should we choose to use AndroidViewModel over ViewModel and vice-versa?
AndroidViewModel provides Application context
If you need to use context inside your Viewmodel you should use AndroidViewModel (AVM), because it contains the application context. To retrieve the context call getApplication(), otherwise use the regular ViewModel (VM).
AndroidViewModel has application context.
We all know having static context instance is evil as it can cause memory leaks!! However, having static Application instance is not as bad as you might think because there is only one Application instance in the running application.
Therefore, using and having Application instance in a specific class is not a problem in general. But, if an Application instance references them, it is a problem because of the reference cycle problem.
See Also about Application Instance
AndroidViewModel Problematic for unit tests
AVM provides application context which is problematic for unit testing. Unit tests should not deal with any of the Android lifecycle, such as context.
Finally I got something a simpler explanation, a bit......
...The AndroidViewModel class is a subclass of ViewModel and similar to them, they are designed to store and manage UI-related data are responsible to prepare & provide data for UI and automatically allow data to survive configuration change.
The only difference with AndroidViewModel is it comes with the application context, which is helpful if you require context to get a system service or have a similar requirement. the bold text makes it clearer to sense it.
AndroidViewModel is subclass of ViewModel. The Difference between them is we can pass Application Context which can be used whenever Application Context is required for example to instantiate Database in Repository.
AndroidViewModel is a Application context aware ViewModel.
AndroidViewModel:
public class PriceViewModel extends AndroidViewModel {
private PriceRepository priceRepository;
public PriceViewModel(#NonNull Application application) {
super(application);
priceRepository= new PriceRepository(application);
allPrices = priceRepository.getAllPrices();
}
ViewModel:
public class PriceViewModel extends ViewModel {
public PriceViewModel() {
super();
}
You Should use AndroidViewModel only when you require Application
Context.
You should never store a reference of activity or a view that references a activity in the ViewModel.Because ViewModel is designed to outlive a activity and it will cause Memory Leak.
Apart from the difference that AndroidViewModel gives you an application context whereas ViewModel does not. The important thing that you must understand is that Google itself recommends using ViewModel and not AndroidViewModel.
So, don't use AndroidViewModel unless it is really necessary.
See this: GOOGLE DOC
Related
The documentation states the following:
If the ViewModel needs the Application context, for example to find a system service, it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor, since Application class extends Context.
Code example:
class MainViewModel(application: Application) : AndroidViewModel(application) {
...
}
Two questions:
How does the AndroidViewModel helps me if I need to pass Application to ViewModel's ctor anyway?
And again, if I need to pass Application, why do I need AndroidViewModel? I can just use ViewModel and pass it Application.
If you're providing your own factory, you can pass anything you want to a regular ViewModel object, you're correct about that.
However, if you are using the default factories, the source code shows that the default factories only fill in the Application instance for you if your ViewModel extend AndroidViewModel.
You can write your custom ViewModel without extending AndroidViewModel and it will be functionally identical.
Only difference is default ViewModelFactory checks if ViewModel is an instance of AndroidViewModel and invokes one-arg constructor passing down Application context automatically.
While using Room Database i found that classes like Repository and ViewModel used Application Context as arguments in their Constructors.
I Just want to know the reason,why this is done?
And is it a Compulsion to use the application context?
public WordViewModel (Application application) {
super(application);
mRepository = new WordRepository(application);
mAllWords = mRepository.getAllWords();
}
WordRepository(Application application) {
WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
mWordDao = db.wordDao();
mAllWords = mWordDao.getAllWords();
}
Curious to Know the reason behind the stuff
Why can't we use a Activity Context?
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
ViewModel objects are designed to outlive specific instantiations of views or LifecycleOwners. This design also means you can write tests to cover a ViewModel more easily as it doesn't know about view and Lifecycle objects.
Why can we use a Application Context?
If the ViewModel needs the Application context, for example to find a system service, it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor, since Application class extends Context.
See ViewModel
let me give you a lazy answer to this question. Application context lifecycle is tied to the lifecycle of the application whereas activity context is tied to the lifecycle of that activity. Make sure to use the right context to prevent memory leaks . Hope this helps
I have started working on MVVM architecture for android application.I have a doubt that is it right to pass the context to view model ? If not then how can my view model can access the context if needed.
I am doing the following things:
Feed data using some EditText.
Send this data to View model.
View model send this data to repository
Repository storing this data to shared preferences of the device.
As shared preferences required context to instantiate the object.
I am new to this architecture any guidance would be helpful for me, thanks in advance.
I think the use of ApplicationContext is ok, You can extend your ViewModel from AndroidViewModel and whenever you need a reference to the context use getApplication() methods.
Even better, if your using dagger, you don't need this at all, you just inject your ApplicationContext where ever you need it. It can be in your view model or a utility class that handles shared preference, etc.
UPDATE 2019
I don't want to mislead anyone with my old post, so figured I better update it.
With the latest release of the architectural components and JetPack, MVVM is now a real competitor for actual proper structure of breaking apart the code base in a very clean way.
The View is the Activity or Fragment along with the xml that is inflated basically (that's a bit of a weird MVVM, I know, but let's roll with it).
The ViewModel is the actual ViewModel classes offered now with lifecycle scopes and observers. Due to these new features, and life cycle management tools, the ViewModel is very nice at maintaining the data for the view while accessing the Repository Layer of Room DB or Retro API for fetching appropriate LiveData, Observable Data or just standard Models.
I'll leave this here as I feel it was very relevant during the earlier pre Architecture Implementation Days, but if you are not using the Architecture Components and JetPack, Boy are you missing out lol. Code is getting so much cleaner and smaller. :)
OLD REPLY
This is a long debated discussion among the Android community. When we refer to MVC it is obvious that Models (aka data holding objects) are the M, Controllers = C for the Activity classes (in iOS they actually call them UIViewControllers) and the V for the View in Android is the XML file itself. Now some would argue the architectural breakdown of what represents what. However, let's move past this and discuss MVVM.
MVVM has been around for many years now. There have been a few attempts in the past to bring it to Android through 3rd party binding tools, but it ends up more hacky then it is worth.
Recently with the release of native Data Binding Android is finally capable of doing a somewhat clean implementation of MVVM. So there are a couple options here. You can do
M = Models (data holding objects)
V = Views (the XML file itself representing the UI)
VM = This is where debates occur.
Some say in order to be a "true" ViewModel one must have true separation from the Presentation layer and the Activity class itself has lifecycle callbacks thus making it unworthy of being known as the ViewModel.
Others would point out that in order to handle most of the actions triggered from the ViewModel one must have an awareness of onActivityResult, onNewIntent, onBroadcastReceived, onPause or other life cycle handlings to appropriately manage the user experience. So On my team we consider the Activity as the ViewModel. Otherwise you are passing the activity down to a viewmodel and tightly coupling the two anyways, making a giant hideous maintenance nightmare of code.
So you if you want my opinion, stick to treating the Activity as your ViewModel. Get your data to the ViewModel just like any other binding technology like INotifyPropertyChanged in WPF. You do it through your Binding.
I do two things to make this happen. One I have an Activity variable in the XML layout to inject in the onCreate for the binding setup which gives the XML direct binding rights to any observable properties in the viewModel aka the activity. Then I inject whatever variables I need to make use of as well for example you may have a WeatherModel that populates a forecast that lives within the Activity that you would also set in the onCreate to allow the XML access to both it's ViewModel (aka activity) and it's viewModel's Objects.
The other alternative is to make a ViewModel object and inject it in the XML in the onCreate of your Activity and then continue to call back and forth from the activity to the viewmodel for handling actions and lifecycles and feel free to manage that nightmare haha, I did an entire project this way and by the end of it, I redid it all to avoid all the duplication of coding efforts and hideous back and forths.
Goodluck and I hope that helps.
A very good question, and it is not as simple as it seems!
You can see an example from Google team here.
They solved the problem with the help of the factory.
There it is pass (of course) the Application context (not Activity context !).
A small problem - and so much boilerplate code!
My decision:
public class MainApplication extends Application {
public void onCreate() {
AppSharedPref sharedPref = AppSharedPref.getInstance(PreferenceManager.getDefaultSharedPreferences(this));
AppRepository.getInstance(sharedPref);
Repository is singltone (a lot of code is skipped for brevity):
public class AppRepository implements AppDataSource {
public static AppRepository getInstance(#NonNull AppSharedPref sharedPref) {
if (INSTANCE == null) {
INSTANCE = new AppRepository(sharedPref);
}
return INSTANCE;
}
In ViewModel call:
public class MyViewModel extends AndroidViewModel {
// constructor
public MyViewModel(#NonNull Application application) {
repository = AppRepository.getInstance(.....);
}
Look into Dagger 2!
That's true , you definitively shouldn't pass Activity to your xml or ViewModel. It will make your ViewModel no better than these 2000-line activities that we are trying to move away from with this architecture.
The solution we have in our MVVM project is to inject SharedPreferences with Dagger. You can of corse use ApplicationContext as well but do you need multiple instances of SharedPreferences in the project??
We a have a utility class for SharedPreferences and it is nice to keep it a singleton and inject wherever you need it.
I use MVVM in my application. I always try not to use Context inside my View Model. I also encountered the problem with SharedPreferences requiring a context to access the preference files. One of my solutions without using Dagger is to create a Preference utility class that will have reference to the application context. You initialize this utility class in you Application class. You get reference to the shared preference through a public static method provided by the utility class. You can directly call the utility class from your repository class. I prefer to contain all logic related to data storage in the repository class that's why i call the sharedpreference utility class in my repository class.
PreferenceManager.java
public class PreferenceManager {
private static SharedPreferences mSharedpreferences;
private PreferenceManager() {}
public static void initialize(Context context) {
mSharedpreferences= context.getSharedPreferences(context.getPackageName(),
Context.MODE_PRIVATE);
}
public static SharedPreferences getSharedPreferences() {
return mSharedpreferences;
}
}
App.java
public class App extends Application {
#Override
public void onCreate() {
PreferenceManager.initialize(this);
}
}
Repository.java
public class Repository {
public void someMethod() {
PreferenceManager.getSharedPreferences.edit.putBoolean("sample", true).apply();
}
You should use AndroidViewModel() in the ViewModel, pass application:Applicatoin, and use getApplication() to get the context.
ViewModel Example:
class MainTrendsViewModel (application: Application) : AndroidViewModel(application) {
// ViewModel must extend AndroidViewModel(), and pass application to it.
// Code here and
// use getApplication() to get context
}
Other anwer that work for me that you could reference: reference answer that works for me
You should use AndroidViewModel class or keep reference to Application context in yours ViewModel implementation in this case.
ViewModel class was designed to keep data persistent between different instances of Activity through its lifecircle and storing reference to one of Activity instance context really doesn't make sense.
I'd like to put my database connection object to Application class. I read some tutorials and everywhere I see explicit implementation of a singleton pattern. Something like this
public class MyApplication extends Application {
private static MyApplication singleton;
public static MyApplication getInstance(){
return singleton;
}
#Override
public void onCreate() {
super.onCreate();
singleton = this;
}
}
My question is why do I need to do this explicit implementation? Nobody explains.
I'd like to add there some property, initialized in onCreate() and get it's value in activity.
myProp = (MyApplication)getApplicationContext().getMyProperty()
Is it really necessary to implement singleton pattern?
An Application class is an access point to application context and generally it is used as a initializator for all application-scoped dependencies like your database object. This class is initialized only once per application and persists in memory until application is no longer in memory. So it is a natively created singleton.
By having such static access point to application you may have access to the application context in any class and in general case this context is much better for retrieving resources, system services etc. when you need such dependencies in your custom classes, because it doesn't hold a link to the activity and view so is a leak-safe. Of course in your example the Activity has an access to application, but you have to cast context anyway and better use same approach for a whole app.
Despite you may use it like a global access point to all application scoped and initialized dependencies, do not overload it with huge initialization logic, better create some other singletons for this purpose and just initialize it from Application class. In a big project consider using DI framework, Dagger the best one, for providing dependencies.
By default the application class is singleton..You are not have allowed to create application yourself.Android System will create this when the first time app is loaded into the memory.You are using singleton pattern here just to get Application object from anywhere within the application
I'm studying google's architecture components to implement ViewModel and LiveData to my app, and the official documentation says that:
Note: Since the ViewModel outlives specific activity and fragment instantiations, it should never reference a View, or any class that may hold a reference to the activity context. If the ViewModel needs the Application context (for example, to find a system service), it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor (since Application class extends Context)
Following that, I ended up with a code like that:
public class ViewModelTest extends AndroidViewModel {
public ViewModelTest(Application application) {
super(application);
}
public void test(){
Prefs.getCurrentCode(getApplication());
}
And should I instantiante it normally on the activity?
val viewModel2 = ViewModelProviders.of(this).get(ViewModelTest::class.java)
viewModel2.test()
Isn't it bad? To use this application variable when need to access SharedPreferences or anything that need a context?
And if it is, should I avoid using it on the ViewModel and use it only on the view? Specially if I want to update a UI component with a value that needs a context. I kinda don't know how to approach this issue, and I'm open for any suggestions.
Thanks in advance
AndroidViewModel class is provided as part of the android.arch.lifecycle package which is part of Android's architecture components. It itself calls for the Application Context passed into the constructor. The Application Context lives across the Activity lifecycle.
An Application context in a ViewModel is okay because the Application context is tied to the whole Application lifecycle as opposed to an Activity context, which is tied to the Activity lifecycle.
ViewModel documentation specifically is referring to not use the Activity Context, but the Application Context if fine.