preface

Warning! This is not a dry article!

Personally, learning technology should not be too impetuous. For a technology blindly pursuing dry goods is not necessarily useful, the goods are too dry easy choking, ha ha ~ it is better to slow down step by step, a little to understand the cause and effect of the technology. (This series is for readers who know Dagger2 but have not used it on a large scale in their projects.)

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

I thought I could handle a project at work by reading a few documents and writing a few demos… I was wrong, I will never have such a stupid idea again… So many dependencies, who can carry ah! So do it little by little.

The body of the

After the first two articles, I’m guessing you’re familiar with the following code:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
  void inject(MainActivity mainActivity);
  SharedPreferences getSharedPrefs(a);
}

@Module
public class AppModule {
    Application application;
 
    public AppModule(Application application) {
       this.application = application;
    }
 
    @Provides
    Application providesApplication(a) {
       return application;
    }
    
    @Provides
    @Singleton
    public SharedPreferences providePreferences(a) {
        return application.getSharedPreferences(DATA_STORE,Context.MODE_PRIVATE);
    }
}


DaggerAppComponent appComponent = DaggerAppComponent.builder()
     .appModule(new AppModule(this)) 
     .build();
Copy the code

Very basic, very simple Dagger application. But is the appModule(new appModule(this)) particularly annoying? When I use Amway, I say dependency injection, but I can’t see new. I have written so much, but it is still new.

AppModule(new appModule(this)) B: Sure. For those of you who have used Dagger Android, it’s pretty clear that you can’t really see any new. So in this article, let’s look at how not to use new completely.

And of course that’s how the Dagger Android works

@Component.Builder

The premise for all of this is this note. What does this note do? Specifically, provide Module dependencies to Component.

Let’s start by thinking about the meaning of the following code:

DaggerAppComponent appComponent = DaggerAppComponent.builder()
     .appModule(new AppModule(this)) 
     .build();
Copy the code

Dagger generates an instance for the dependency we want, so of course the entire dependency network needs to be built. No matter how magical the Dagger is, it is impossible to build the entire dependency we need out of thin air, so we need to “provide and guide” at the right time. So new AppModule(this) provides Dagger, since Dagger doesn’t know how to instantiate the AppModule from our code above, so we need to provide it.

Then again, we can tell it how to instantiate the AppModule in the above code, so that we do not have to manually go to new AppModule(this).

Yeah, that’s what @Component.Builder does.

I’m exhausted. I’ve come all the way around and I don’t know if you understand the @Component.Builder.

AppModule(Application application)

For our AppModule, the key to instantiating it is how to provide an Application.

public AppModule(Application application) {
   this.application = application;
}
Copy the code

The same is true for Dagger, the reason it can’t instantiate the AppModule is because it doesn’t know or can’t get an Application instance. Therefore, for Dagger, we should give it an instance of The Application, not the AppModule.

Began to transform

The first step is to transform the AppComponent using @Component.Builder:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
   void inject(MainActivity mainActivity);
   SharedPreferences getSharedPrefs(a);
   // Change the content
   @Component.Builder
   interface Builder {
        AppComponent build(a);
        // This line of code has not been modified yet
        Builder appModule(AppModule appModule); }}Copy the code

Now we tell you that Dagger you have to instantiate the AppComponent we want as the interface of the @Component.Builder annotation.

Notice how the added methods look like the code generated by the DaggerAppComponent? Yes, the Dagger default instantiation of the AppComponent is done in this code.

However, for our AppModule, we don’t need to know how it is initialized (because we only need the dependencies it provides). The same is true for Dagger: All Dagger needs to give us is an Application instance to provide a dependency tagged by @provides. Since you have an Application instance Dagger there is a way to instantiate the AppModule, just new.

So here we need a way to tell Dagger: I’m going to give you what I need inside the @Module. So for our demo, we need a way to give him the Dagger Application.

And the person who did that was @bindsinstance.

@BindsInstance

According to the official website, this annotation identifies a method in Component Builder/SubComponent Builder that allows instance binding to Component.

So for our AppModule, it just needs to provide @Provides! All it needs is a Dagger that will be injected into it as instructed. It looks like this:

@Module
public class AppModule {
     @Provides
     @Singleton
     // External Application instance passed (Dagger injected as instructed)
     public SharedPreferences providePreferences(Application application) {
         return application.getSharedPreferences("store", Context.MODE_PRIVATE); }}Copy the code

Our AppConponent does just that:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
   void inject(MainActivity mainActivity);
   SharedPreferences getSharedPrefs(a);
   @Component.Builder
   interface Builder {
        AppComponent build(a);
        @BindsInstance 
        // The application will be injected into the providePreferences method of the AppModule
        Builder application(Application application); }}Copy the code

After build, we initialize the DaggerAppComponent by writing:

DaggerAppComponent appComponent = DaggerAppComponent.builder()
   .application(this)
   .build();
Copy the code

So how does Dagger generate the DaggerAppComponent for us?

public final class DaggerAppComponent implements AppComponent {
  private Provider<Application> applicationProvider;

  private Provider<SharedPreferences> providePreferencesProvider;

  private DaggerAppComponent(Builder builder) {
    initialize(builder);
  }

  public static AppComponent.Builder builder(a) {
    return new Builder();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.applicationProvider = InstanceFactory.create(builder.application);
    this.providePreferencesProvider =
        DoubleCheck.provider(
            AppModule_ProvidePreferencesFactory.create(builder.appModule, applicationProvider));
  }

  @Override
  public void inject(MainActivity mainActivity) {}

  @Override
  public SharedPreferences getSharedPrefs(a) {
    return providePreferencesProvider.get();
  }

  private static final class Builder implements AppComponent.Builder {
    private AppModule appModule;

    private Application application;

    @Override
    public AppComponent build(a) {
      if (appModule == null) {
        this.appModule = new AppModule();
      }
      if (application == null) {
        throw new IllegalStateException(Application.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }

    @Override
    public Builder application(Application application) {
      this.application = Preconditions.checkNotNull(application);
      return this; }}}Copy the code

For the AppModule, it simply comes out new, and when we need @Provides, we just pass in the required application. Our application stays inside the DaggerAppComponent “body” as a member variable.

The end of the

This post isn’t really about @Component.Builder and @bindsInstance. Instead, try to use them to deepen your understanding of components and modules. Feel the Dagger implementation to better understand dependency injection. For us, we need to use the form and learn the god.

So that we are not constrained by the framework, but driven by it.

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 ~