Looking at the source code for the Android Architecture Components sample GithubBrowerSample, I don't understand the point of double injecting the githubApp.
Wouldn't the inject method be enough? Why do it need both of them in the same sentence?
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(GithubApp githubApp);
}
And they use it like:
public static void init(GithubApp githubApp) {
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp);
As Thomas Broyer described, you've got two separate directions to set up: You want the Dagger graph to know how to get to your Application instance, and you want to get access to certain bindings out of the dependency graph.
The #BindsInstance line in the Builder creates a binding for Application set to the instance you pass in. It sounds like you understand this part.
However, after you've created your Component, presumably you want to use it. Let's say you want to get fully-injected instances of classes Dep1, Dep2, and Dep3 out of your graph. One way you could do this is to create methods on your Component that get the instances:
#Singleton #Component(/* ... */) interface AppComponent {
// [builder snipped out here]
fun getDep1(): Dep1
fun getDep2(): Dep2
fun getDep3(): Dep3
}
And then you call those as part of your App creation.
var appComponent = DaggerAppComponent.builder().application(githubApp).build()
var dep1 = appComponent.getDep1()
var dep2 = appComponent.getDep2()
var dep3 = appComponent.getDep3()
// Use dep1, dep2, and dep3 here.
However, you can also create a single-arg method, which is typically a void method called inject. This populates all of the #Inject-annotated fields and calls all of the #Inject-annotated methods on the instance you pass in. If GitHubApp has #Inject-annotated-fields (and it does), the call to inject lets you skip defining all of the getters on the Component. That reduces all of the above code to:
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp)
...which is what you see in the demo.
The #BindsInstance tells Dagger that it should inject the application into whichever #Inject Application it finds in the dependency graph.
The second asks Dagger to inject dependencies into it's #Inject-annotated fields and methods. This the root of the dependency graph.
Technically, the component method can be called as many times as you like, while the builder method can only be called once.
Related
I've been trying to find a way to provide dependencies depending where a fragment is navigated from. I have the following in a library module:
interface FruitProvider {
fun provide(): String
}
class FruitFragment : Fragment() {
#Inject
lateinit var provider: FruitProvider
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
}
The idea is for other modules to consume this library and inject whatever they want as a FruitProvider.
As an example, if I have 2 providers:
class BananaProvider #Inject constructor() : FruitProvider {
override fun provide() = "banana"
}
class PearProvider #Inject constructor() : FruitProvider {
override fun provide() = "pear"
}
If I'm in the banana module I'd like to create and display an instance of FruitFragment injected with BananaProvider. Similarly for the pear module but with a PearProvider.
Background
Right now we have a method in FruitFragment to set the provider, i.e., setFruitProvider(provider: FruitProvider) that is called by the consumers of the library.
This approach is not ideal because if the activity gets recreated this field is null. At least this is what some debugging seems to suggest. Put simply, the fragment is recreated but the setFruitProvider is not being called again since it gets called from methods outside the fragment's lifecycle.
The interface FruitProvider cannot be Serializable so we cannot save it in the instance state. All that we think is left is to inject the fragment since this can be done by the fragment itself when attached.
So I was working on some sample MVVM project using Dagger. I have a viewmodel factory that goes like this:
class DaggerViewModelFactory #Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = viewModelsMap[modelClass] ?:
viewModelsMap.asIterable().firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
A viewmodel factory module
#Module
abstract class ViewModelFactoryModule {
#Binds
abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
I got a ViewModelModule:
#Module
abstract class MyViewModelModule {
#Binds
#IntoMap
#ViewModelKey(TakePicturesViewModel::class)
abstract fun bindTakePictureViewModel(takePicturesViewModel: TakePicturesViewModel): ViewModel
}
A component that goes like this:
#PerActivity
#Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
fun inject(mainActivity: MainActivity)
}
An a viewmodel that goes like this:
class TakePicturesViewModel #Inject constructor(app: Application): AndroidViewModel(app) {...
So I can either inject my viewmodel in my activity using a view model factory like this:
#Inject
lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
Or with not viewmodel factory at all, like this:
#Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
Both ways work, so I was wondering which one is the right way to work, if using Dagger allows me to inject a viewmodel without needing a viewmodelfactory, is there a good reason to keep it?, or should I just get rid of this viewmodelfactory?
Thanks in advance for any advice.
Greetings
Both ways work, so I was wondering which one is the right way to work, if using Dagger allows me to inject a viewmodel without needing a viewmodelfactory, is there a good reason to keep it?, or should I just get rid of this viewmodelfactory?
Both ways work differently. Try rotating your screen with stored data in your ViewModel and you'll see.
Dagger can create the ViewModel, which is what you make use of in that generic ViewModelFactory. Those view models should be unscoped, thus you'll be creating a new ViewModel every single time. The Android support library will cache that ViewModel and reuse it after rotation so that you can keep your data—the factory method gets called once and there will only ever be one ViewModel created (per lifecycle). You keep your data and everything behaves as expected.
If on the other hand you use Dagger to inject your ViewModel directly none of the above will apply. Like any other dependency, a new ViewModel will be injected on creation, leading to a ViewModel being created every single time it is used—you'll not only use the data stored in it, you won't be able to share state with fragments either.
Of course you could apply a scope to the ViewModel, but that scope should be longer lived than the Activity instance (to keep state between rotations), but no longer lived than the screen is visible. So you can neither scope it to the activity nor to the application lifecycle. You can get it to work by introducing a new scope in-between, but at this point you'd be reinventing the ViewModel library.
tl;dr Inject and use the factory or you'll get a confusing/wrong ViewModel implementation.
In reference to Dagger 2 Subcomponent Documentation, the example code shows that
#Singleton
class RequestRouter {
#Inject RequestRouter(
Provider<RequestComponent.Builder> requestComponentProvider) {}
void dataReceived(Data data) {
RequestComponent requestComponent =
requestComponentProvider.get()
.data(data) // Data could be set here?
.build();
requestComponent.requestHandler()
.writeResponse(200, "hello, world");
}
}
From what I understood, that Subcomponent.Builder can only set the Module and Build, as per https://dagger.dev/api/2.10/dagger/Component.Builder.html
Just wonder how could we pass Data into the builder?
Always make sure to read the latest JavaDoc, 2.10 is quite old (Mar 20, 2017). I believe #BindsInstance was introduced with Dagger 2.12:
[...]
There may be setter methods annotated with #BindsInstance. These methods bind the instance passed to them within the component. See #BindsInstance for more information.
[...]
You can add various bindings to your component, e.g.
#BindsInstance
Builder foo(Foo foo); // allow binding of some `Foo`
According to Dagger documentation about injecting activity objects, it says that installing AndroidInjectionModule in your application component. However, everything is fine without it.
Does it means that I don't need to declare it? Under what circumstances will it be wrong?
For example:
Injected instance
data class Food(val name: String)
Module
#Module
class FoodModule{
#Provides
fun provideFood(): Food {
return Food("cholocate")
}
}
BindingModule
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector(modules = [FoodModule::class])
abstract fun FoodShop(): MainActivity
}
AppComponent (Without installing AndroidInjectionModule)
#Component(modules = [MainActivityModule::class])
interface AppComponent{
fun inject(app: App)
}
App
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.create().inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> {
return dispatchingActivityInjector
}
}
MainActivity
class MainActivity : AppCompatActivity() {
#Inject
lateinit var food: Food
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("test", "Get ${food.name}")
}
}
It get chocolate successfully in MainActivity.
Does it means that I don't need to declare it? Under what circumstances will it be wrong?
It actually seems like you don't need to declare it, but it might lead to compile errors if you don't.
If you have a look at AndroidInjectionModule you can see that it just lists a bunch of #Multibinds methods for framework types.
#Multibinds
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
activityInjectorFactories();
Now if you look up Declaring #Multibinds you can read that
You do not have to use #Multibinds for sets or maps that have at least one #IntoSet, #ElementsIntoSet, or #IntoMap binding, but you do have to declare them if they may be empty.
And to declare them if they may be empty is exactly what the AndroidInjectionModule module is doing for you. If the Android Injection parts would require an undefined Map of injector factories you would probably get a compile time error stating that it cannot be provided.
The reason that you don't need the module is because you're using #ContributesAndroidInjector, of which the generated code will contain a #Binds #IntoMap etc. method, that declares the bindings map. Stated above—as it is not empty anymore—you would not need the additional #Multibinds declaration that AndroidInjectionModule provides for the non-empty multibinding.
You might not need the module, but it will declare all the framework injector factories for you in case that they might be empty, possibly preventing one or two compile errors. After all the javadoc simply states that it should be installed, not that it must.
This module should be installed in the component that is used to inject the Application class.
Dagger 1's plus() method is something I used quite often in previous applications, so I understand situations where you might want to have a subcomponent with full access to the parent graphs bindings.
In what situation would it be beneficial to use a component dependency instead of a subcomponent dependency and why?
Component dependencies - Use this when you want to keep two components independent.
Subcomponents - Use this when you want to keep two components coupled.
I will use the below example to explain Component dependencies and Subcomponents. Some points worth noticing about the example are:
SomeClassA1 can be created without any dependency. ModuleA provides and instance of SomeClassA1 via the provideSomeClassA1() method.
SomeClassB1 cannot be created without SomeClassA1. ModuleB can provide an instance of SomeClassB1 only if an instance of SomeClassA1 is passed as an argument to provideSomeClassB1() method.
#Module
public class ModuleA {
#Provides
public SomeClassA1 provideSomeClassA1() {
return new SomeClassA1();
}
}
#Module
public class ModuleB {
#Provides
public SomeClassB1 provideSomeClassB1(SomeClassA1 someClassA1) {
return new SomeClassB1(someClassA1);
}
}
public class SomeClassA1 {
public SomeClassA1() {}
}
public class SomeClassB1 {
private SomeClassA1 someClassA1;
public SomeClassB1(SomeClassA1 someClassA1) {
this.someClassA1 = someClassA1;
}
}
Dagger will take care of passing the instance of SomeClassA1 as an argument to provideSomeClassB1() method on ModuleB whenever the Component/Subcomponent declaring ModuleB is initialized. We need to instruct Dagger how to fulfill the dependency. This can be done either by using Component dependency or Subcomponent.
Component dependency
Note the following points in the Component dependency example below:
ComponentB has to define the dependency via the dependencies method on #Component annotation.
ComponentA doesn't need to declare ModuleB. This keeps the two components independent.
public class ComponentDependency {
#Component(modules = ModuleA.class)
public interface ComponentA {
SomeClassA1 someClassA1();
}
#Component(modules = ModuleB.class, dependencies = ComponentA.class)
public interface ComponentB {
SomeClassB1 someClassB1();
}
public static void main(String[] args) {
ModuleA moduleA = new ModuleA();
ComponentA componentA = DaggerComponentDependency_ComponentA.builder()
.moduleA(moduleA)
.build();
ModuleB moduleB = new ModuleB();
ComponentB componentB = DaggerComponentDependency_ComponentB.builder()
.moduleB(moduleB)
.componentA(componentA)
.build();
}
}
SubComponent
Note the following points in the SubComponent example:
As ComponentB has not defined the dependency on ModuleA, it cannot live independently. It becomes dependent on the component that will provide the ModuleA. Hence it has a #Subcomponent annotation.
ComponentA has declared ModuleB via the interface method componentB(). This makes the two components coupled. In fact, ComponentB can only be initialized via ComponentA.
public class SubComponent {
#Component(modules = ModuleA.class)
public interface ComponentA {
ComponentB componentB(ModuleB moduleB);
}
#Subcomponent(modules = ModuleB.class)
public interface ComponentB {
SomeClassB1 someClassB1();
}
public static void main(String[] args) {
ModuleA moduleA = new ModuleA();
ComponentA componentA = DaggerSubComponent_ComponentA.builder()
.moduleA(moduleA)
.build();
ModuleB moduleB = new ModuleB();
ComponentB componentB = componentA.componentB(moduleB);
}
}
According to the documentation:
Component Dependency gives you access to only the bindings exposed as provision methods through component dependencies, i.e. you have access to only types which are declared in parent Component.
SubComponent gives you an access to the entire binding graph from its parent when it is declared, i.e. you have an access to all objects declared in its Modules.
Let's say, you have an ApplicationComponent containing all Android related stuff (LocationService, Resources, SharedPreference, etc). You also want to have your DataComponent where you manage things for persistence along with WebService to deal with APIs. The only thing you lack in DataComponent is Application Context which resides in ApplicationComponent. The simplest way to get a Context from DataComponent would be a dependency on ApplicationComponent. You need to be sure you have a Context explicitly declared in ApplicationComponent because you only have access to declared stuff. In this case, there is no manual work, meaning you don't need to specify Submodules in parent Component and explicitly add your submodule to a parent module like:
MySubcomponent mySubcomponent = myComponent.plus(new ChildGraphModule("child!")); // No need!
Now consider that case where you want to inject WebService from DataComponent and LocationService from ApplicationComponent into your Fragment which binds using the #Submodule plus feature above. The cool thing here is that the component you're binding to (ApplicationComponent) does not need to expose WebService nor LocationService because you have access to the entire graph right away.
Here is the code example with screenshot for more understanding of Component and SubComponent:
Component:
AppComponent contains two declarations.
AppComponent initializes into App class.
HomeActivityComponent is dependent upon AppComponent.
In HomeActivity on initialization of DaggerHomeActivityComponent, I am giving AppComponent object as a composition.
SubComponent:
AppComponent contains SubComponent or SubComponents.
AppComponent initializes into App class.
SubComponent doesn’t know about his ParentComponent. That only providing its own dependencies by including Module.
In HomeActivity I am injecting SubComponent by using its Parent Component.
And the Pictorial Diagram:
Source: link
One other thing that I didn't quite realize until now is that:
A #Subcomponent instance has exactly one parent component (although different components can instantiate that same #Subcomponent and be that instance's parent)
A #Component may have zero, one, or many "parent" components declared through component dependencies