preface

To be honest, this is a framework with mixed reviews. The steeper learning curve is not proportional to the benefits. And once you can’t carry a full court, it’s easy to operate like a tiger, a look at the record 0-5…

So I decided to temporarily stop the following Dagger article after this article.

A framework must exist to solve a certain kind of problem. This is similar to @Component and @SubComponent. @SubComponent doesn’t exist when we can use @Component for our daily development, unless someone “has a brain” and makes a wheel.

So @SubComponent is bound to solve problems @Component can’t solve…

The body of the

Dagger 2: Component Relationships & Custom Scopes is referenced in this article

You need to prepare a ladder

In the previous series, we used Dagger2 step by step:

Sooner or later, you have to pay back the technical debt

Sooner or later, you have to pay back the technical debt.

Dagger2: Android chapter (中) @scope, @singleton

Dagger2: Android chapter 2 (below) further understand the Dagger

One, faint problems

At the beginning of the project, the origin of all things, small business needs, tai Chi born two instruments, two instruments born four elephants, four elephants born eight diagrams… The code structure is simple, easy to write and easy to maintain.

When we use Dagger at this point, we usually start with a single AppComponent and a single AppModule with an @Singleton.

In this case, A Dagger generates A DaggerAppComponent that HAS A HAS-A relationship with the AppModule. Totally oJBK, and it’s fun to write, straight to the point!

But as our App grows, we’ll soon realize that the AppModule is starting to become a God Module with dependencies. More and more requirements, more and more dependencies need to be added to the AppModule, all the useful ones need to be added…

Refactoring is imminent…

Some solutions come naturally here: now that our AppModule is starting to become a God module with various dependencies. Then we dismantle Module not ok.

Generally speaking, Module disassembly can be divided into horizontal and vertical disassembly.

1.1. Horizontally disassemble Module

We might refactor like this:

@Component(modules = {AppModule.class, ApiModule.class})
public interface AppComponent {
    / / to omit
}
Copy the code

1.2. Vertically disassemble Module

@Component(modules = AppModule.class)
public interface AppComponent{
    / / to omit
}

@Module(includes = ApiModule.class)
public class AppModule{
    / / to omit
}
Copy the code

This seems, “perfect solution to our problem”? But is that the case? The questions are always varied:

If we need to provide singletons with different life cycles for different modules at different @ scopes. All we can do is stare! Component is unique no matter how Module is removed, which means Component’s Scope is unique.

So we need to tear it apart further, which is the Component.

Component general scheme

Scenario 1: Low-level Components depend on high-level Components

The idea of this scheme is very similar to our normal “unmodule” idea. That is, abstractions are extracted from the higher-level Component and then sunk into the lower-level Component.

The implementation of this scheme will be described in detail below.

Scheme 2: @SubComponent

Let our different Components have parent/subclass inheritance with @subComponent.

The implementation of this scheme will be described in detail below.

Let’s go through a real example to get a feel for the split Component between these two scenarios.

2.1 “Demand Review”

Suppose we now need a user module that handles user login/logout and so on.

The content in the red box is nothing to worry about. It is actually a “user’s name”.

To briefly explain the flow of the diagram, from left to right, the application is started. Then pass the LoginComponent “pikachu” (Pikachu… This user logs in and goes to HomeComponent, which is the normal action page, as is the ItemComponent behind it (just another module). Then enter LoginComponent again and log out… Next “JiigglyPuff” login and repeat the process…

2.2. Code dependency graph

  • AppComponent(@Singleton) : The Singleton scoped component, the main component of the application. Typically created and persisted on Application.

  • LoginComponent(@activityScope) : Subcomponent of AppComponent. Contains content that only LoginActivity needs to rely on.

  • UserComponent(@userScope) : depends on AppComponent(but not SubComponent). Contains user module-related dependencies. This component also contains two other Subcomponents(@actVityScope) : HomeComponent and ItemsComponent.

  • HomeComponent(@activityScope) : Subcomponent of UserComponent. It also contains three fragments: ProfileFragment, StatsFragment, and MovesFragment.

  • ItemsComponent(@activityScope) : Subcomponent of UserComponent.

Based on the above text, let’s look at a picture:

2.3 Code implementation scheme 1: Low-level Component depends on high-level Component

2.3.1, AppComponent

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    // for @subComponent, press no list first
    fun loginBuilder(a): LoginComponent.Builder

    BaseSchedulerProvider (BaseSchedulerProvider)
    fun schedulerProvider(a): BaseSchedulerProvider
}
Copy the code

The AppComponent is our top Component and is bound to be relied upon by other Components. But other Components can’t hold an instance of The AppComponent directly and, in turn, can’t call the dependencies it provides through the AppComponent instance.

This is not inheritance, so there is no way to call variables/methods of a parent class like a subclass.

That makes sense, right? Just like the implementation between modules in our Android project. If a lower-level module wants to use a higher-level module, we usually write a Service in the lower-level module, and then the higher-level module implements this interface. The concrete classes that implement the interface are then polymorphically registered into the underlying module. This completes the low-level module calling the high-level module.

The same is true for our Dagger. If the underlying Component wants to use our AppComponent, it also needs to provide such interface methods in the AppComponent. The Fun schedulerProvider(): BaseSchedulerProvider.

We’ll get a feel for this method later in the subcomponent (UserComponent).

2.3.2, MyApplication

Instantiation of the top-level Component, AppComponent.

class MyApplication : Application() {
    lateinit var appComponent: AppComponent
        private set
        
    override fun onCreate(a) {
        super.onCreate()
        appComponent = DaggerAppComponent
		.builder()
		.application(this)
		.build()
    }
    
    companion object {
        lateinit var app: MyApplication
            internal set}}Copy the code

2.3.3, UserComponent

@Component(dependencies = arrayOf(AppComponent::class), modules = arrayOf(UserModule::class))
@UserScope
interface UserComponent {
    @Component.Builder
    interface Builder {
        fun build(a): UserComponent;
        
        @BindsInstance
        fun pokeMon(pokemon: Pokemon): Builder
    }
}
Copy the code

Pokemon is an entity Class of user information, and this Class contains information returned from our user login. (Don’t care about it, just YY in your mind.)

A quick explanation:

  • Dependencies = arrayOf(AppComponent::class)

  • 2. We declare @Component.Builder in UserComponent. For the UserComponent, only one Pokemon instance is required for its instantiation, so only one @bindsInstance is required.

The use of Component.Builder/ @bindsinstance was mentioned in a previous post.

Let’s take a closer look at the code:

2.3.4, UserManager

Instantiation of UserComponent.

@Singleton
class UserManager @Inject constructor(private val service: PokemonService) {
    var userComponent: UserComponent? = null
        private set

    public fun createUserSession(pokemon: Pokemon){ userComponent = DaggerUserComponent.builder() .appComponent(MyApplication.app.appComponent) .pokeMon(pokemon) .build() }}Copy the code

Fun schedulerProvider(): BaseSchedulerProvider:

How does the Dagger provide dependencies for our underlying Component generation via the Fun schedulerProvider(): BaseSchedulerProvider method in AppComponent?

Build the DaggerUserComponent class directly:

public final class DaggerUserComponent implements UserComponent {
    private Provider<BaseSchedulerProvider> schedulerProvider;

    private void initialize(final Builder builder) {
	    this.schedulerProvider =
    	    		new AppComponent_schedulerProvider(builder.appComponent);
	    // Omit some code
    }

    private static class AppComponent_schedulerProvider 
					implements Provider<BaseSchedulerProvider> {
        private final AppComponent appComponent;
	
        @Override
	    public BaseSchedulerProvider get() {
 		    return appComponent.schedulerProvider();
	    }
	    // Omit some code}}Copy the code

We can see the Fun schedulerProvider() in the AppComponent: BaseSchedulerProvider

schedulerProvider in DaggerUserComponent; Exists in the form of.

Provider < BaseSchedulerProvider > is an interface that will be implemented in the form of class within, and one of the get method, is through appComponent schedulerProvider () available.

AppComponent is passed along with Builder in Initialize (Final Builder Builder). When we instantiate DaggerUserComponent namely: appComponent (MyApplication. App. AppComponent)

Code implementation scheme to this end, I don’t know if you guys get ~ next let’s have a look at the code implementation scheme two.

2.4. Code implementation scheme 2: @subComponent

Against 2.4.1, LoginComponent

@ActivityScope
@Subcomponent(modules = arrayOf(LoginModule::class))
interface LoginComponent {
    @Subcomponent.Builder
    interface Builder {
        @BindsInstance
        fun loginActivity(loginActivity: LoginActivity): Builder
    }
}
Copy the code

That’s not enough, we also need to modify the AppComponent, otherwise our Dagger doesn’t know how to instantiate the LoginComponent. The transformation is very simple, remember what we mentioned above?

OK, so our LoginComponent is done writing.

2.4.2, LoginActivity

Instantiation LoginComponent

class LoginActivity {
    private lateinit var loginComponent: LoginComponent

    fun initDagger(appComponent: AppComponent) {
        loginComponent = appComponent
            .loginBuilder() // The method we left in the AppComponent
            .loginActivity(this)
            .build()
   }
    // omit unnecessary content
}
Copy the code

Let’s look at the DaggerAppComponent again:

public final class DaggerAppComponent implements AppComponent {
  	this.loginBuilderProvider =
      		new Factory<LoginComponent.Builder>() {
        		@Override
        		public LoginComponent.Builder get() {
         	 		returnnew LoginComponentBuilder(); }}; }private final class LoginComponentBuilder implements LoginComponent.Builder {
		// Omit some code
	}
    // Pay attention to understand here
	private final class LoginComponentImpl implements LoginComponent {
		privateLoginComponentImpl(LoginComponentBuilder builder) { assert builder ! =null;
  			// Initialize the dependency provided by the LoginComponent
			initialize(builder);
		}
  		// Omit some code}}Copy the code

Dagger generates a LoginComponentImpl, which is an inner class of DaggerAppComponent. Since an inner class can access member variables of its outer class, any AppComponent dependencies required by the LoginComponent can be accessed directly from the AppComponent without explicitly exposing them.

There is no need for scheme 1: Fun schedulerProvider(): BaseSchedulerProvider

For the LoginComponent, as long as it gets the dependencies it needs from the AppComponent, there is no need for additional declarations.

So let’s go back and look at the picture posted at the beginning:

For The LoginComponent, it is an inner class of the AppComponent, so you can get what you need directly from the AppComponent.

UserComponent, on the other hand, requires the AppComponent to provide dependencies explicitly so that the UserComponent can get the dependencies it needs.

3. Comparison and summary

DaggerUserComponent:

  • DaggerUserComponent depends on AppComponent.
  • The DaggerUserComponent does not know any implementation of the AppComponent, only the dependencies exposed through the AppComponent interface.
  • The DaggerUserComponent can only call dependent methods provided by the AppComponent through the schedulerProvider.

LoginComponentImpl:

  • LoginComponentImpl is a child of AppComponent.
  • LoginComponentImpl is an inner class of DaggerAppComponent.
  • LoginComponentImpl gets its dependencies directly from the AppComponent without requiring any additional content.

So, for us. We can use @subComponent when there is a lot of coupling between components. Instead we can use scenario 1: Component dependency.

The end of the

So this is the end, and I don’t know how many of you can see this all the way. I’ve been “muddled” while writing this series. When writing this article, I gradually have some suddenly enlightened feeling.

As an aside, does Dagger actually work for us on Android? When our project is big enough and the various implementations are cumbersome enough. If we have proper dependency injection, we can easily use an object. Here’s a small example from our project:

I need an implementation class for an interface that looks something like this:

interface IItemAction {
    fun showActionsDialog(activity: Activity.// omit a lot of arguments)
}
Copy the code

If I need to manually new the IItemAction, we need to pass all the arguments it needs, and some of the arguments need to be instantiated with more arguments…

If our project had a decent dependency injection, I would just do this:

@Inject
lateinit var action: IItemAction
Copy the code

This article is over.

I am a fresh graduate, recently and friends maintain a public account, the content is that we in the transition from fresh graduate to the development of this way stepped on the pit, as well as our step by step learning records, if interested in friends can pay attention to it, together with fuel ~