I'm learning dagger and I fount this article that explains how to use android.dagger everything is clear for me except with custom created scopes. Previously I saw a lot of tutorials where custom scope is created to create dependencies to specific situations (e.g Logged In scope). But that tutorial showed my other approach. Here's my example:
I have a class that should be generated only for MainActivity (and MasterActivity) but not for LoginActivity
class SecretKey(
val token: String,
val userId: Long
)
So here's the module
#Module
class MainActivityModule {
#Provides
fun provideSecretKey(preference: SharedPreferences): SecretKey {
return SecretKey(
"jwtToken",
465465
)
}
}
and ActivitiesBindingModule
#Module
abstract class ActivitiesBindingModule {
#ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun mainActivity(): MainActivity
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
abstract fun loginactivity(): LoginActivity
// MasterActivity will see everything from MainActivityModule and LoginActivityModule
#ContributesAndroidInjector(modules = [MainActivityModule::class, LoginActivityModule::class])
abstract fun masterActivity(): MasterActivity
}
So as I understood only in MainActivity and MasterActivity I will be able to inject SecretKey class because of the ContributesAndroidInjector module. So SecretKey scope is within MainActivity and MasterActivity. So why we still are able to create custom scopes with Scope annotation? Is this alternative?
Scope simply tells Dagger to save the instance of the scope-annotated object rather than creating a new one. Dagger will save the instance in the component of the corresponding scope. (You should also know that #ContributesAndroidInjector code-generates a Subcomponent instance for you, so if you annotate the #ContributesAndroidInjector method with a scope annotation, the generated subcomponent will take that scope.)
In your example, SecretKey is not scoped; every time you ask Dagger to inject a SecretKey it will call your constructor and create a brand new instance. This is probably fine, as SecretKey seems not to keep state, and keeping the instance scopeless allows the garbage collector to collect SecretKey when you no longer need it.
However, imagine that you create your own Cache object, and that you want that Cache to live as long as the activity but not longer: Each new Activity instance should get its own Cache. Unlike SecretKey, you could not create your own Cache every time one is requested; you'd need to save your cache instance somewhere. You could choose to do this as a field on the Activity itself, or you could make a Module instance that saves an instance value the first time you call a #Provides method, but Dagger prefers that you mark the binding with a scope annotation that matches the component. This allows you to declare and document that the binding will have the same lifetime as the component, and makes it easy to categorize bindings as "Application-scoped", "Activity-scoped", "Fragment-scoped", "Service-scoped", and so forth.
Related
In Android Hilt, you can apply the #Singleton annotation to a function like this:
#Module
#InstallIn(SingletonComponent::class)
object SomeModule {
#Singleton
#Provides
fun provideSomething(): String {
return "Hi there"
}
}
I don't understand what the purpose of using a singleton on a function accomplishes. A class that has the #Singleton means that an instance of the class only exists once. But you cannot create instances of functions, so I don't see the point.
#Singleton is called Scope and by default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type. For example in your case each time you request a String a new string is going to be created.
But when you add #Singleton annotation you said that the scope of this instance is singleton so it's going to provide the same instance each time you request a String.
Unlike SingletonComponent which is a component, it decide where you can use that module, so if you change it to ViewModelComponent for example you will be able to use that module only from ViewModel and it's lifecycle is going to be related to the ViewModel lifecycle.
You can take a look here for more information: https://developer.android.com/training/dependency-injection/hilt-android#generated-components
Take a look at this example:
instead of just providing a static string try to provide some random number:
#Provides
fun provideString(): String {
return (1..100).random().toString()
}
Without #Singleton when you inject String in different classes in your App you will get different results because each time hilt is going to run the code inside provideString function so it's going to return a new instance of String ( that's what I mean by providing a new instance ).
Now add #Singleton:
#Singleton
#Provides
fun provideString(): String {
return (1..100).random().toString()
}
You will notice that the provided String is the same across your app so provideString is called once and the result is saved so hilt is returning that saved instance of String every time.
Functions can be singletons just like classes , It will assure only one instance will be created .
Normally multiple calls of same function will be stacked , and performed one by one , while in a singleton function , last call will overwrite the previous one .so if you called it 10 times in a loop , no instances of the calls will be stacked in the memory.
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'm doing an app using Kotlin and Dagger2, trying to follow the MVVM pattern, but I'm in a dilemma, should I use #Singleton, object or both? And why? Let's say I have a RepositoryMovies class and I want to get the same instance every time, according to my research you can do this as follows:
#Singleton (Dagger2 way)
#Singleton
class RepositoryMovies {
TODO()
}
Object (Kotlin way)
object RepositoryMovies {
TODO()
}
Both
#Singleton
object RepositoryMovies {
TODO()
}
And don't get me started with singletons in Kotlin following the "Java-Way". I'd appreciate your help. Thanks so much.
Injecting an object doesn't make much sense, since in kotlin object is used to simulate java's utility classes, such as java's Arrays or Collections classes. One defining characteristic of such classes is that, they are not associated with any specific class in your project, they can be required and used anywhere.
On the other hand in most practical situations a repository will be associated with a specific class. for example you may only want to inject a UserRepository in a UserViewModel, because that is the only place where you need to access user's information.
As for the object and #Singleton, object is by definition a singleton, so marking it with #Singleton is redundant and doesn't accomplish anything until you make it injectable by means of a #Provides function. where you have to specify, how dagger can create instances of this class?
In your first example marking a class #Singleton doesn't do anything, unless it is injectable. as the docs state.
Singletons and Scoped Bindings
Annotate an #Provides method or injectable class with #Singleton. The graph will use a single instance of the value for
all of its clients.
dagger-android 2.16
I have a dependency cycle error in my Dagger module. I think I know what the problem is but not sure how to solve it.
This is the error message:
Found a dependency cycle:
public interface LoginFragmentSubcomponent extends AndroidInjector<LoginFragment> {
presentation.login.request.LoginRequest is injected at
mobileui.login.di.LoginActivityModule.provideLoginResponseListener(…, loginRequest)
presentation.login.response.LoginResponseListener is injected at
mobileui.login.di.LoginActivityModule.provideLoginRequest(…, loginPresenter)
presentation.login.request.LoginRequest is injected at
mobileui.login.di.LoginActivityModule.provideLoginPresenter(…, loginRequest)
mobileui.login.LoginPresenter is injected at
mobileui.login.LoginFragment.loginPresenter
This is the module below where I am getting the error
#Module
class LoginActivityModule {
#Reusable
#Provides
fun provideLoginPresenter(loginRequest: LoginRequest): LoginPresenter {
return LoginPresenterImp(loginRequest)
}
#Reusable
#Provides
fun provideLoginResponseListener(loginRequest: LoginRequest): LoginResponseListener {
LoginPresenterImp(loginRequest)
}
#Reusable
#Provides
fun provideLoginRequest(loginUser: LoginUser,
loginPresenter: LoginResponseListener): LoginRequest {
return LoginRequestImp(loginUser, loginPresenter)
}
}
My LoginPresenterImp implements the LoginResponseListener and I want to pass that to the LoginRequestImp class so I can use it as a callback.
class LoginPresenterImp(private val loginRequest: LoginRequest) :
BasePresenterImp<LoginView>(),
LoginPresenter,
LoginResponseListener {
}
And the loginResponseListener gets passed here:
class LoginRequestImp(
private val loginUser: LoginUser,
private val loginResponseListener: LoginResponseListener)
: LoginRequest {
}
Many thanks in advance,
As Ayush described in the comments:
You need LoginResponseListener to create LoginRequest and you need LoginRequest to create LoginResponseListener. So, you are getting the error.
When you are creating LoginRequest in LoginRequestImp(loginUser, loginPresenter), loginPresenter is a parameter to the constructor of type LoginResponseListener. You should try to eliminate this dependency. May be you can set the listener later from the presenter
In your reply between those comments:
LoginRequest has been created in provideLoginRequest
But this is what's happening:
Your LoginFragment tries to inject LoginPresenter.
Before you inject LoginPresenter you need to create a LoginRequest.
Before you create a LoginRequest you need a LoginUser and a LoginRequestListener.
Before you create a LoginRequestListener (which you've implemented as a LoginPresenterImpl), you need a LoginRequest.
You're in the middle of creating a LoginRequest, so Dagger gives up and correctly reports a circular reference.
To reiterate: Even though you've set up the bindings using interfaces correctly, Dagger can't create any of them because to call either constructor it has to create the other. This isn't a problem with Dagger: If class A's constructor takes an instance of B, and class B's constructor takes an instance of A, you couldn't construct either of them manually either while respecting their constructor parameters.
As Ayush suggested, don't have LoginRequest inject a LoginResponseListener. Instead, create a method like setLoginResponseListener, which LoginPresenterImp can call. I recommend this approach as well, in part because #Reusable has weaker semantics than you want: You want to be absolutely sure that the LoginPresenterImp instance that acts as your LoginPresenter is the same instance that acts as LoginResponseListener.
As an alternative, you can inject Provider<LoginPresenter> instead of LoginResponseListener, and change LoginRequestImp to accept a Provider as well. (You could also inject a Provider<LoginResponseListener>, but if you want that LoginResponseListener to be the same one as your LoginPresenter instance, you shouldn't call the LoginPresenterImp constructor explicitly. You'd want to switch to #Binds ideally, or at least have your #Provides method inject LoginPresenter instead.) You're allowed to inject a Provider, because a Provider<T> is automatically bound for every class <T> that Dagger knows how to provide, and it solves your problem because Dagger can pass a Provider<T> without trying to create the T. This will technically appear to work even if you leave your bindings as #Reusable, but in a multithreaded environment #Reusable isn't going to guarantee that you always receive the same instance for LoginRequestListener as your LoginPresenter, or that you'll receive a new LoginPresenter for every LoginFragment. If you want to guarantee that, you can look into custom scopes.
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.