In my app i have an ApplicationScope which provides me with stuff like context and shared prefs.
2 sub components, LoggedInComponent and LoggedOutComponent.
Those 2 subcomponents have other subcomponents which are less relevant to the issue.
I have a NetworkModule which creates my retrofit instance.
Both subcomponents need the NetworkModule but the retrofit might change during the login because the base url and other settings might change.
I was wondering which approach it is better to take:
1. Give both LoggedIn and LoggedOut sub components and the NetworkModule the same scope so both sub components can use this module. Feels a bit hacky and wrong usage of scopes.
2. Put network module in the AppComponent with ApplicationScope. Also wrong usage of scope because network module is recreated so it cannot have same scope and also each recreation will cause the AppComponent and its subcomponents to be recreated.
What would you do? maybe there is a better solution?
You don't want LoggedInComponent and LoggedOutComponent to have differing network behavior, and you don't necessarily need network behavior to be consistent across the lifetime of LoggedInComponent or LoggedOutComponent, so it doesn't make sense for you to have them bind NetworkModule separately. I think it makes sense to make them available through AppComponent. Remember that not everything in AppComponent needs to have ApplicationScope: You can make unscoped bindings in AppComponent, which instructs Dagger to re-fetch or re-create the requested object every time.
Specifically, I would bind NetworkModule into a subcomponent of AppComponent, which you can recreate every time the NetworkModule configuration changes. Not only would this let you encapsulate some of the network details (see "subcomponents for encapsulation" in the Dagger User's Guide), but it would also let you take advantage of dependency injection throughout your network bindings if that's what you're looking for.
To ensure that you're getting the correct current NetworkComponent, you could create a NetworkManager that is singleton-bound (ApplicationScope). You can use that to hold the latest NetworkComponent.
#Module(subcomponents={NetworkComponent.class})
public abstract class ApplicationModule {
/** Unscoped. Fetch this fresh from NetworkComponentHolder each time. */
#Provides Retrofit provideRetrofit(NetworkManager manager) {
return manager.getNetworkComponent().getRetrofit();
}
}
#ApplicationScope public class NetworkManager {
private final Provider<NetworkComponent.Builder> builderProvider;
private NetworkComponent currentComponent;
#Inject public NetworkComponentHolder(
Provider<NetworkComponent.Builder> builderProvider) {
this.builderProvider = builderProvider;
currentComponent = builderProvider.get()
.withNetworkModule(getDefault())
.build();
}
public void updateSettings(String baseUrl) {
currentComponent = builderProvider.get()
.withNetworkModule(new NetworkModule(baseUrl))
.build();
}
public NetworkComponent getNetworkComponent() {
return currentComponent;
}
}
With this, most of your code in AppComponent, LoggedInComponent, and LoggedOutComponent can just inject a Retrofit (or Provider<Retrofit>) whenever they need to make a request. When a response comes back that tells you to update your base URL, you can inject a NetworkManager, call updateSettings, and suddenly new requests for Retrofit will return your new instance. (Note however that old instances to Retrofit may still stick around, but you'll have that problem any time you're changing a dependency belonging to an existing instance.)
p.s. If NetworkModule is lightweight enough, or has consistent-enough bindings, you might opt to put NetworkModule directly onto ApplicationComponent and simply have NetworkManager hold the current Retrofit instance etc. You'll have to make that judgment call based on the number of bindings you want to pass through as provideRetrofit does, compared to the number of bindings you'd want to encapsulate or hide away in a subcomponent.
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 a Dagger2 #Module class with the #Provides annotated method which calls Retrofit.create method:
#Provides
RestService provideRestService(final Retrofit retrofit) {
return retrofit.create(RestService.class);
}
Should I annotate this method with the #Singleton annotation?
I see one reason to do it: calling create each time has some cost and one reason for not doing it: keeping one instance has some cost (Dagger performs double checking each time instance is requested).
Which solution is preferred? With or without #Singleton annotation? Or maybe it is not important at all? Or my approach to create this class in provider is fundamentally wrong?
If additional instances are allowed but potentially-expensive, you might also consider the use of #Reusable. You lose double-checked locking, but you (potentially) shorten object lifespans and gain flexibility about where the instances are installed.
#Reusable is useful when you want to limit the number of provisions of a type, but there is no specific lifetime over which there must be only one instance.
See the User's Guide topic or the SO question Dagger #Reusable scope vs #Singleton.
Yes, you should. You avoid memory allocation. Android will keep you the same instance untill the app is killed. Think about if you call the api like into a for loop and each time you create a new instance (not recommended and won't happen when you use dagger in same injectionm but just like an example). It will kill your memory. So you should use #Singleton.
Singleton will ensure you that you will use the same instance across module.
I'm currently developing an Android MVP Application, and I'm trying to separate my dependencies in different Dagger2 Modules.
The problem I'm having is about changing a module in Unit Test Time. The scenario is the following:
LoginComponent, which uses two modules: LoginModule and HTTPModule
LoginModule in one of its methods requires an OkHttp instance, which is provided by HTTPModule.
The code is the following:
#Singleton
#Component(modules = {LoginModule.class, HTTPModule.class})
public interface LoginComponent {
}
#Module(includes = {HTTPModule.class})
public class LoginModule {
#Provides
#Singleton
public MyThing provideMyThing(OkHttpClient client) {
// Do things with it
}
}
#Module
public class HTTPModule {
#Provides
#Singleton
public OkHttpClient provideOkHttpClient(){
// Return the OkHttpClient
}
}
The thing is, at test time I would need to change the OkHttpClient that is returned (by making it accept all the certificates, as when I run it on the JVM it does not accept the LetsEncrypt certificate).
Also I would need that because I need to declare that MyTest.class can be injected with module, and as MyTest.class is under the app/src/test/ folder, it's not visible for the classes that are placed under app/src/main/. What I've done until now is to copy and paste the Component and the modules to the /test/ folder, and make the injected class declaration there. But I know there must be a proper way to achieve what I'm looking for.
Another thing I've tried is annotating the methods with custom Scopes (creating a #TestScope annotation). However this leads me to the same problem that I had commented before: I cannot make the MyTest.class visible to the component, because it's placed under the /test/ folder.
I've already checked other similar questions, such as this one and this another one, but this last one is for running tests with Robolectric, and by now I'm able to unit test most of my code with JUnit4 only (Android Studio 2-Beta 8).
If anyone could point me to the right direction, I would be more than grateful.
Thanks in advance!
You're using Dependency injection in a way that still keeps your code tightly coupled. Generally speaking you want your dependencies to be interfaces instead of actual classes. This keeps your code nice and loose, easy to read, modify and maintain.
Hide your network operations behind an interface to allow you to modify the network implementation whenever you need to. This could be done for testing - in your case, but it will also allow you to switch out the network library if you'll want to or need to in the future without changing any other code.
Try something like this:
#Module
public class HTTPModule {
#Provides
#Singleton
public NetworkProvider provideNetworkProvider(){
// Return the Network provider
}
}
The network abstraction layer:
public interface NetworkProvider {
// Methods to send requests and receive async responses
}
The OkHttp implementation:
public class OkHttpNetworkProvider implements NetworkProvider {
// Implement NetworkProvider. This is the only class that
// knows about OkHttp and its components
}
Now you can create a mock version of NetworkProvider and use it for testing, whether via a test module or directly.
I’m new to Dagger 2. I have this scenario, I wan't to inject an object across my app (in presenters, in api)
I do not have a way to provide it initially. It is not created till after authentication at some stage in my app.
From the documentation http://google.github.io/dagger/
I see Lazy loading might be a way to solve this e.g
#Inject
Lazy<Grinder> lazyGrinder;
and then get the value like this using:
lazyGrinder.get().grind();
My questions are:
Can I safely swap the object after this with a new one?
Are there any other recommended ways to do this?
Thanks
This isn't a good match for Lazy. Lazy is a great way to delay expensive object initialization, but it implies some semantics that you don't want or need, particularly regarding the "safely swap" behavior you want.
To put it simply, Lazy is a Provider wrapper that memoizes locally:
If you never call get, Dagger never creates the object in question.
The first call to get creates and stores the object instance.
The second call to get returns the same instance, and so on forever, regardless of whether the object was marked as Singleton.
This makes Lazy an excellent choice for an expensive object that would otherwise be a field (but may never be used). However, if the reference is likely to change (as your will), Lazy will simply be confusing: It will store the value at first use and never locally update, so multiple out-of-date copies might be floating around in your application regardless of what the "right" value is at any given time.
To borrow the use of Grinder from your example, better solutions include:
Using a #Provides method that returns a field in a Module, which can be updated later. You'll need to inject Provider<Grinder> for every long-lived object instance, because injected references to Grinder alone won't update. This still might be the best bet if you have a lot of short-lived objects.
The reference is implicitly singleton, but is not annotated as such, because you're controlling the instance yourself. Dagger will call your getGrinder method frequently.
#Module public class YourModule {
private Grinder grinder;
public void setGrinder(Grinder grinder) {
this.grinder = grinder;
}
#Provides public Grinder getGrinder() {
return grinder;
}
}
/* elsewhere */
YourModule module = new YourModule();
YourComponent component = DaggerYourComponent.builder()
.yourModule(module)
.build();
/* ... */
module.setGrinder(latestAndGreatestGrinder);
As EpicPandaForce mentioned in the comments, create/bind a singleton GrinderHolder, GrinderController, or AtomicReference object that provides the current instance and allows for updating. That way it's impossible to inject a Grinder directly, but easy and obvious to inject the object that fetches the current correct Grinder. If your singleton GrinderHolder implementation doesn't create the Grinder until the first time you ask for it, then you have effectively created a Lazy singleton on your own.
If you aren't able to provide the object at the time of Component creation, don't add it to your Component graph! That is asking for confusing graph dependencies and inconsistency. A better solution to what you are considering is a #Subcomponent approach, which allows you to create a new component which inherits the dependencies from the parent, but also adds new one. Here's an example:
#Component
interface RegularComponent {
#AppInstanceId String appInstanceId(); // unique per app install; not related to logging in
AuthenticatedComponent newAuthenticatedComponent();
}
#Subcomponent
interface AuthenticatedComponent {
Set<Friend> friends();
#AccountId String accountId();
}
Here, the #AccountId in the subcomponent could use the appInstanceId to provide the account ID (if it needed to) since the Subcomponent shares dependencies with its parent component.
If you need to supply state to your modules for the subcomponent (with the accountId, auth token, etc) feel free to pass it in as a parameter to the #Module and store it in a private final field. You can read more on how to supply subcomponent modules in the documentation.
I've looked at a couple different articles which seem to suggest two different ways of doing custom scoping in Dagger 2:
MVP Presenters that Survive Configuration Changes Part-2 (Github repo):
Uses unique custom scopes for each fragment, e.g. #Hello1Scope and #Hello2Scope for Hello1Fragment and Hello2Fragment respectively
Tasting Dagger 2 on Android:
Uses a single custom scope for all fragments, e.g. #PerFragment.
From what I understand, it seems that, as in method 2, it should be okay to have a single scope defined that can be used for all fragments (i.e., #PerFragment). In fact (please correct me if I'm wrong), it seems like the name of the custom scope is irrelevant, and it's only where the subcomponent is created (i.e. in Application, Activity, or Fragment) that matters.
Is there any use case for defining a unique scope for each fragment such as in case 1?
After reading the answer by #vaughandroid, and What determines the lifecycle of a component (object graph) in Dagger 2? I think I understand custom scopes well enough to answer my own question.
First, here are a couple rules when dealing with components, modules, and scoping annotations in dagger2.
A Component must have a (single) scope annotation (e.g. #Singleton or #CustomScope).
A Module does not have a scope annotation.
A Module Method may have a (single) scope that matches its Component or no scope, where:
Scoped: means a single instance is created for each instance of the component.
Unscoped: mean a new instance is created with each inject() or provider call
NOTE: Dagger2 reserves #Singleton for the root Component (and it's modules) only. Subcomponents must use a custom scope, but the functionality of that scope is exactly the same as #Singleton.
Now, to answer the question: I would say create a new named scope for each conceptually different scope. For example, create a #PerActivity, #PerFragment, or #PerView annotation that indicates where the component should be instantiated, and thus indicating its lifetime.
Note: this is a compromise between two extremes. Consider the case of a root component and n subcomponents you will need:
at least 2 annotations (#Singleton and #SubSingleton), and
at most n+1 annotations (#Singleton, #SubSingleton1, ... #SubSingletonN).
Example:
Application:
/** AppComponent.java **/
#Singleton
#Component( modules = AppModule.class )
public interface AppComponent{
void inject(MainActivity mainActivity);
}
/** AppModule.java **/
#Module
public class AppModule{
private App app;
public AppModule(App app){
this.app = app;
}
// For singleton objects, annotate with same scope as component, i.e. #Singleton
#Provides #Singleton public App provideApp() { return app; }
#Provides #Singleton public EventBus provideBus() { return EventBus.getDefault(); }
}
Fragment:
/** Fragment1Component.java **/
#PerFragment
#Component( modules = {Fragment1Module.class}, dependencies = {AppComponent.class} )
public interface Fragment1Component {
void inject(Fragment1 fragment1);
}
/** Fragment1Module.java **/
#Module
public class Fragment1Module {
// For singleton objects, annotate with same scope as component, i.e. #PerFragment
#Provides #PerFragment public Fragment1Presenter providePresenter(){
return new Fragment1Presenter();
}
}
/** PerFragment.java **/
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerFragment {}
Your understanding is correct. The named scopes allow you to communicate intention, but they all work the same way.
For scoped provider methods, each Component instance will create 1 instance of the provided object.
For unscoped provider methods, each Component instance will create a new instance of the provided object whenever it needs to inject it.
The lifetime of the Component instance is important though. 2 different instances of the same component will provide different object instances, even scoped ones.
Scope names should indicate the lifetime of the provided object (which matches that of the Component instance) so #PerFragment makes much more sense to me.
From a quick look at the "MVP Presenters..." tutorial, it's not clear to me exactly what the author's intention is with having separate scopes. Since the names are just throwaway ones, I wouldn't read too much into it.