I'm doing some codelabs of kotlin fundamentals and I don't really get in android with the ViewModel why sometimes there seems to be a need of creating it through a ViewModelFactory. Here you can see the codelab which talks about this.
They just say to perform the initialization using the factory method pattern but I don't understand the why. Why do we need to use factory pattern? Is it because we need to pass some parameter to the ViewModel? Or is it for some other reason? Is every time we need to create a ViewModelFactory just to pass parameters to the ViewModel?
I've been looking for the answer, trying to confirm whether is just to pass extra parameters or is because of any other reason but still I'm not sure and I haven't found the answer.
There are a few things that need to consider before using ViewModel and ViewModelFactory
ViewModel is LifecycleAware Components.
ViewModel survive configuration changes.
ViewModelProvider' can only instantiateViewModel` with no-args contructor.
Why do we need to use factory pattern?
To instantiate ViewModel with arguments need to use ViewModelFactory. ViewModelProviders Utility can not create an instance of a ViewModel with argument constructor because it does not know how and what objects to pass in the constructor.
Also, you should follow the Dependency Injection principle. A class should not create dependencies it needs. It should be provided rather than creating.
For Example -
public class LogInViewModel extends ViewModel {
private final LogInRepo repo;
public LogInViewModel (LogInRepo repo) {
/* this.repo = new LogInRepo(); Not recommended, It violates DI principle*/
this.repo = repo;
}
}
Related
I've seen many tutorials for MVVM. Most of them say that you need to define your ViewModel class like this:
class MainViewModel: ViewModel() {
...
}
But recently I stumbled upon Dagger tutorial project from Google. There is a different ViewModel class definition:
class MainViewModel(private val userDataRepository: UserDataRepository) {
...
}
So I wonder, what is the difference between these two approaches?
That's not a relevant comparison. That CodeLab uses a non-ViewModel ViewModel class to simplify their explanation of how DI works. Notice it doesn't subclass ViewModel. Also, the project starts without the dependency injection and has you add it in later, so the starting project isn't intended to be a good example of how to design something.
Either way, if you have a repository, you need some way to get a reference to the repository in your ViewModel. If it is through the constructor, you would have to get a reference to the repository in the associated ViewModelFactory that you build for this class. If you use Dagger, you'll probably let Dagger generate this factory for you and inject the reference.
If your ViewModel doesn't use a repository, then you won't have any reason to have one in your constructor, with or without dependency injection. Many basic MVVM tutorials are going to start with the most basic possible example, a ViewModel with no arguments needed. That doesn't imply that a ViewModel should never have dependencies.
I have multiple viewmodels that access a single repository (one activity and the rest fragments).
AdminActivityViewModel
AdminListUsersViewModel
AdminUserTransactionsViewModel
... and a few more
My AdminRepo class has multiple constructors so that I can pass callback methods from the ViewModel
public AdminRepo(Application application, AdminActivityCallback callback) {
this.callback = callback;
BaseApplication baseApplication = (BaseApplication) application;
RetrofitClient client = baseApplication.getRetrofitClient();
adminService = client.getRetrofit().create(AdminService.class);
SharedPrefManager sharedPref = SharedPrefManager.getInstance(application);
AuthHeader authHeader = new AuthHeader(
sharedPref.getIdToken(),
sharedPref.getIdClient(),
sharedPref.getUserEmail()
);
client.setAuthHeader(authHeader);
}
public AdminRepo(Application application, AdminListUsersCallback callback) {
//Exact same code in constructor as above ^
}
public AdminRepo(Application application, AdminUserTransactionsCallback callback) {
//Exact same code in constructor as above ^
}
And in each of the ViewModels I am creating an instance of the AdminRepo (which is probably a bad practice) but I don't know how I can improve this.
public class AdminActivityViewModel extends AndroidViewModel implements AdminActivityCallback
public AdminActivityViewModel(#NonNull Application application) {
super(application);
repo = new AdminRepo(application, this);
}
How can I design my AdminRepo and ViewModels so that they share exactly one repository without creating an expensive AdminRepo class everytime?
I have considered making my AdminRepo class a singleton with a .getInstance() method, but I'm getting SO MANY contradicting posts on how Repository classes SHOULD NOT be static or a singleton, which makes me extremely confused on what I SHOULD do.
https://stackoverflow.com/a/7464235/11110509
If all view models live at the same time then and repository does not keep any state you could share the same instance of repository and provide that instance for each view model. However there's no place for magic here - if you create an instance of repository, you have to keep a reference to that, so you can pass the same object to other view models.
At first you need to make your view model accepting external dependencies (I\d recommend seeing dependency injection pattern)
YourViewModel( /* other dependencies ..., */ AdminRepo adminRepo)
Once you have that, you need to create a view model factory. This post describes it nicely, but long story short: implement ViewModelProvider.Factory that will keep your repository instance and use it in ViewModelProvider to obtain an instance of your view model.
With this setup you will be in control what instance you create and how you create other view models. This could be automated though with the use of dependency injection framework (Koin, dagger, hilt). If you use Dagger or Hilt (recommended by Google), then you can leave your object lifecycle in hands of the framework by providing a proper annotation. Let's try this exaple:
#Singleton
class MyRepository { ...
#HiltViewModel
class MyViewModel {
MyRepository repo;
#Inject MyViewModel(MyRepository repo) { ...
...
This code will make your repository a singleton tied to your application lifecycle. Usually you don't want to do that, because the object will live in the memory even if you move to the screen that deosn't need that. But you could use #ActivityScoped so your repository lives as long the activity.
Now if those view models have different lifecycles and they don't overlap, it would be completely fine to create a new insance for each one of them. You wouldn't want to keep unnecessary objects in the memory while you don't need them anymore.
I want to inject application string inside object class SingletonObject. I am new in Hilt and not getting any way to inject this
object SinglentonObject{
#AppQualifier
#Inject
lateinit var applicationString: String
}
The #Inject annotation is available only for EntryPoints like #AndroidEntryPoint, #HiltAndroidApp. As of now, there is no option to inject into a non-entry point classes in Hilt.
You can't use the #Inject annotation but, if you still want to have a single source of truth and use Hilt from anywhere, you can create a custom EntryPoint and then use the ApplicationContext to grab an instance of whatever you are providing with Hilt.
First, you have to declare the EntryPoint (it can be added anywhere you want, but the best practice is to keep it closer to where it's used):
#EntryPoint
#InstallIn(SingletonComponent::class)
interface ApplicationStringInterface {
#AppQualifier fun getApplicationString(): String
}
then you can grab an instance of the ApplicationString like this:
var applicationString = EntryPoints.get(applicationContext, ApplicationStringInterface::class.java).getApplicationString();
If you need help to get an instance of the applicationContext from anywhere, look here: https://stackoverflow.com/a/54076015/293878
To define a singleton, should I use Kotlin object declaration or to make an ordinary Kotlin class and inject it using dagger? In my opinion the first option is definitely easier but there may be a reason to use dagger in this situation that I'm not aware of.
Option 1 (notice object keyword):
object SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
Option 2 (notice class keyword):
class SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
#Module
class AppModule {
#Provides
#Singleton
internal fun provideTheUtil() = SomeUtil()
}
class MainActivity : BaseActivity() {
#Inject internal lateinit var util: SomeUtil
}
UPDATE 2019-07-03
#Blackbelt said in comments that we should prefer option 2 for testability. But libraries like MockK can mock objects too. So do you still think option 2 is the preferred one?
You might want to reconsider the need of NumberFormatUtil being a singleton. It might be cheaper if you use #Reusable with Dagger or even a factory without any scope directly.
If NumberFormatUtil is fairly simple and only provides a few utility methods, no state and no need for mocking in tests, you could use an object implementation, maybe using #JvmStatic for Java-inter-operability. But then you could go for global utility (extension) functions as well:
package xyz
fun formatNumber(number: Long) {
// ...
}
fun Long.format() = formatNumber(this)
You should use option 2.
In software engineering, the singleton pattern is a software design
pattern that restricts the instantiation of a class to one "single"
instance. This is useful when exactly one object is needed to
coordinate actions across the system.
From: Singleton Pattern
So, a singleton is single instance in a scope. In case of Android, it is virtual machine instance running the app. In case you need custom scopes, you have to use option 2 only.
But, if you have only static methods inside the object you want to inject its better to keep them as global methods and even get rid of object. No need to inject anything. It is similar to a java class with only static methods (I mentioned this point as it is a usual way of creating Utility classes).
However, if the object also has some state. I would recommend going dagger way. The object way does not provide with dependency injection. It only creates a Singleton. Your purpose for using dagger is dependency injection.
Interesting how difficult this answer is to find.
I've been using Dagger - Android for a while now and have my entire dependency graph set up. I'm using scopes, qualifiers, all that good stuff. I'm not a Dagger newbie anymore, but suffice to say I've been using it in a pretty standard way in my Android setup and everything has been going great.
For the first time, I'm realizing that I'd like to request new instances of a certain class in my graph myself, manually, and I want it to be a new instance every time.
What's the best way to go about doing that? I'm wondering if there's a way to leverage a non-#Singleton/non-scoped provider and call some sort of create() method myself, or if it's best to create a factory myself and make that factory a singleton/scoped instance and use my factory to get the new instance(s) when I need them? [I should mention this class will definitely not have an empty constructor so will need injected instances of other classes defined in my injection graph.]
(Also, it would probably help the most if answers were in the context of Android; i.e. I'm in, say, a ViewModel and need a new instance of some class defined in one of my Modules.)
Dagger will provide you a new instance as long as you don't scope the dependency.
To get a new instance of a dependency manually, you can inject Provider of it instead and use its get() method, it'll give you a new instance every time you call it.
Module part doesn't really change:
#Module
class AppModule {
#Provides
fun provideSomeObject(): SomeObject = SomeObject()
}
And in your class
class SomeClass {
// We don't inject the object anymore
// #Inject lateinit var myObject : SomeObject
// We'll inject it's provider
#Inject lateinit var myObject : Provider<SomeObject>
fun someMethod(){
// Here, instance1 and instance2 are NOT same objects
val instance1 = myObject.get()
val instance2 = myObject.get()
}
}
You can read more here.