Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
One: lazy/reload
Lazy loading in 1.1 Dagger2
Intelligent lazy loading is one of the important measures for Dagger2 to achieve high performance: member variables are initialized only when needed, which can greatly reduce the application initialization time.
Use Lazy to modify variables. Lazy is a generic class that accepts arguments of any type.
@Inject
Lazy<Object> object;
Copy the code
In the example of the demo in Section 3.2 of Dagger2 Tool Series 1: Getting Started with Using, just use Lazy to modify the objects that need to be injected.
public class Car {
/ * * *@Inject:@InjectIt is used to mark dependent variables and tell Dagger2 to provide it with a dependency /@InjectLazy<Engine> engine; public Car() { DaggerCarComponent.builder().build().inject(this); } public Engine getEngine() { return this.engine; } public static void main(String ... args){ Car car = new Car(); System.out.println(car.getEngine()); }}Copy the code
1.2 Provider Forces reloading
The Singleton implementation of the @Singleton annotation allows us to get the same object every time (without going into the global/local Singleton), but sometimes we want to create a new instance every time, which is the exact opposite of @Singleton. Dagger2 can be implemented using a Provider. It is used in a similar way to Lazy.
Use Provider to modify variables. Providers are generic classes that accept arguments of any type.
@Inject
Provider<Object> object;
Copy the code
Using the example of the demo in section 3.2 of Dagger2 Tools Series 1: Getting Started to Use, just use the Provider to modify the object that needs to be injected.
public class Car {
/ * * *@Inject:@InjectIt is used to mark dependent variables to tell Dagger2 to provide it with dependencies */
@Inject
Provider<Engine> engine;
public Car(a) {
DaggerCarComponent.builder().build().inject(this);
}
public Engine getEngine(a) {
return this.engine;
}
public static void main(String ... args){
Car car = newCar(); System.out.println(car.getEngine()); }}Copy the code
If the @Provides method returns the same object every time, it will return the same object every time you call get().
Component organizational dependencies
2.1 introduction
The organization dependency of Component mainly refers to the Dagger 2 fully resolved (3). The organization relation of Component is the same as the article SubComponent. I think it is well written and the example is very good. I suggest you check it out. Enter the main text.
In a real project, there would be multiple objects that need to inject dependencies. That is, there would be multiple components with the same dependencies among them. How would you handle the relationship between them? Take this scenario as an example:
public class Man {
@Inject
Car car;
public void goWork(a) {... car.go(); . }}public class Friend {
@Inject
Car car; // I borrowed the car from Man
public void goSightseeing(a) {... car.go(); . }}Copy the code
The project scenario is as follows: Man has a Car, Friend does not have a Car, but he can borrow Man’s Car to go out to play, but the CarModule that provides Car instances remains unchanged.
How do we design the Component in this case? The first thing many people would do is:
// @manScope and @friendScope are both custom scopes
@ManScope
@Component(modules = CarModule.class)
public interface ManComponent {... }@FriendScope
@Component(modules = CarModule.class)
public interface FriendComponent {... }Copy the code
The easiest way to do this is that ManComponent and FriendComponent require cars in their modules. That is, Man and Friends both have carModules to provide cars. So at this point, see the problem? This car is not Man’s car anymore!
Question:
(1) Sometimes dependent instances need to be shared. For example, in the scenario above, Friend’s car is borrowed from Man, so FriendComponent should use the car instance in ManComponent.
(2) Scope is easy to fail. For example, the CarModule provideCar() uses the @singleton Scope. FriendComponent and ManComponent also use Singleton annotations. But they both hold an instance of CAR.
So FriendComponent relies on the Car instance provided by ManComponent, which is one of the Component organizational relationships: dependencies.
2.2 Component organization
The organization of the Component in Dagger 2 is divided into two types:
-
Dependencies: A Component depends on other Compoent to get exposed instances of dependencies, declared in Component.
-
Inheritance: One Component inherits (extends) other Components to obtain dependencies in other Components. SubComponent is the embodiment of inheritance.
2.2.1 Dependencies
Dependency diagram in the Friend and Man scenario:
Specific implementation code:
@ManScope
@Component(modules = CarModule.class)
public interface ManComponent {
void inject(Man man);
Car car(a); // We must provide an interface for car dependent instances, indicating that Man can lend cars to others
}
@FriendScope
@Component(dependencies = ManComponent.class)
public interface FriendComponent {
void inject(Friend friend);
}
Copy the code
Note: Since FriendComponent and ManComponent are dependencies, if one declares scope, the other must declare scope as well. And they can’t have the same Scope. ManComponent’s life cycle >= FriendComponent’s. The Scope of FriendComponent cannot be @Singleton because the Component of @Singleton in Dagger 2 cannot depend on other components.
The compile-time Provider implementation of DaggerFriendComponent uses manComponent.car() to provide car instances. If manComponent does not provide an interface for car instances, The DaggerFriendComponent will fail to inject.
Dependency injection:
ManComponent manComponent = DaggerManComponent.builder()
.build();
FriendComponent friendComponent = DaggerFriendComponent.builder()
.manComponent(manComponent)
.build();
friendComponent.inject(friend);
Copy the code
A dependency is just like a friend in your life.
Dependent components need to declare exposed dependency instances with explicit interfaces, like Car Car () above. We can only use what our friends want to share.
The components in a dependency cannot have the same Scope because they have different life cycles.
2.2.3 Inheritance relationship
Inheritance is similar to the concept of inheritance in object-oriented language. Subcomponents are called subcomponents, similar to subclasses. Take a look at the following scenario:
public class Man {
@InjectCar car; . }public class Son {
@Inject
Car car;
@Inject
Bike bike;
}
Copy the code
Son can drive his father’s car, Man. He can also ride his bike. Dependency diagram:
In the figure above, SonComponent is in ManComponent. SonComponent inherits its parent and can access parent Component’s dependencies. ManComponent knows that SonComponent is its child Component and can access subComponent. Builder, but cannot access dependencies in SubComponent.
@ManScope
@Component(modules = CarModule.class)
public interface ManComponent {
void inject(Man man); // Inheritance relationships do not explicitly provide interfaces that expose dependent instances
}
@SonScope
@SubComponent(modules = BikeModule.class)
public interface SonComponent {
void inject(Son son);
@Subcomponent.Builder
interface Builder { // SubComponent must explicitly declare subComponent. Builder. Parent Component needs Builder to create SubComponent
SonComponent build(a); }}Copy the code
@subComponent is written like @Component and can only be annotated with interfaces or abstract classes. Like dependencies, SubComponent cannot have the same Scope as parent Component, except that SubComponent extends a Component.
How to indicate which parent Component a SubComponent belongs to? Add the SubComponent class to the parent Component dependent Module. You can then request the SubComponent.Builder in the Parent Component.
@Module(subcomponents = SonComponent.class)
public class CarModule {
@Provides
@ManScope
static Car provideCar(a) {
return newCar(); }}@ManScope
@Component(modules = CarModule.class)
public interface ManComponent {
void injectMan(Man man);
SonComponent.Builder sonComponent(a); // Create Subcomponent
}
Copy the code
SubComponent compilation does not generate a DaggerXXComponent. You need to get the SubComponent instance from the Parent Component’s Get SubComponent.Builder method.
ManComponent manComponent = DaggerManComponent.builder()
.build();
SonComponent sonComponent = manComponent.sonComponent()
.build();
sonComponent.inject(son);
Copy the code
The biggest difference between inheritance and dependencies is that instead of explicitly providing an interface to the dependency instance, SubComponent inherits all of the parent Component’s dependencies.
2.3 Dependency vs. inheritance
Similarities:
-
Both can reuse dependencies of other Components
-
Components with dependencies and inheritances cannot have the same Scope
The difference between:
-
The dependent Component of a dependency must explicitly provide an interface to expose the dependency instance, and the SubComponent inherits the parent Component’s dependencies by default.
-
Dependencies generate two separate DaggerXXComponent classes, while SubComponent does not generate a separate DaggerXXComponent class.
In Android development, Activity is a component in App running, and Fragment is also a part of Activity. This componential idea is suitable for inheritance, so SubComponent is generally used in Android.
2.4 Other problems with SubComponent
2.4.1 Abstract factory methods define inheritance relationships
In addition to using the Module’s subComponents property to define the inheritance relationship, you can declare an abstract factory method in the parent Component that returns the SubComponent:
@ManScope
@Component(modules = CarModule.class)
public interface ManComponent {
void injectMan(Man man);
SonComponent sonComponent(a); // This abstract factory method indicates that SonComponent inherits ManComponent
}
Copy the code
This definition does not clearly indicate inheritance, and it is generally recommended to use the Module subComponents property definition.
2.4.2 Duplicate Modules
When the same Module is injected into the Parent Component and its SubComponent, each Component automatically uses the same instance of the Module. An error occurs if the same Module is called in subComponent. Builder or if the abstract factory method that returns SubComponent takes a duplicate Module as an argument. (The former cannot be detected at compile time and is a runtime error)
@Component(modules = {RepeatedModule.class, ... })
interface ComponentOne {
ComponentTwo componentTwo(RepeatedModule repeatedModule); // Error when compiling
ComponentThree.Builder componentThreeBuilder(a);
}
@Subcomponent(modules = {RepeatedModule.class, ... })
interface ComponentTwo {... }@Subcomponent(modules = {RepeatedModule.class, ... })
interface ComponentThree {
@Subcomponent.Builder
interface Builder {
Builder repeatedModule(RepeatedModule repeatedModule);
ComponentThree build(a);
}
}
DaggerComponentOne.create().componentThreeBuilder()
.repeatedModule(new RepeatedModule()) / / run times wrong UnsupportedOperationException!
.build();
Copy the code
2.5 summarize
There are two types of organizational relationships when components share the same dependencies: dependencies and inheritance. Android development typically uses inheritance, with AppComponent as the root Component, and AppComponent using the @Singleton scope. The ActivityComponent is the SubComponent.
Reference article:
Dagger2 from start to drop -Component inheritance, local singleton
Dagger 2 has the same organization relation as SubComponent