AAC Learning Notes Dagger (2) -- Injecting Activity

Keywords: Mobile Android Java Attribute Fragment

This article is part of Android Architecture Components Learning Notes
The document code is Kotlin, but the code generated by the system is still Java.
For ease of understanding, I've broken down the official examples step by step.
My level is limited. If there is something wrong with me, please don't hesitate to give me advice.

The last article analyzed Dagger's basic usage, which is still weak and explosive. How to apply it in a project? This article explores the things that Activity object injection does.

Article directory

Application

Explain the GithubApp class in Android Manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ……
    <application
        android:name=".GithubApp"
        ……

The android:name attribute is used to set which application all activities belong to
The GithubApp class has global members, and AppInjector.init(this) in the code executes before activity.
AppInjector.init(this) is also the beginning of all injections.

The first step, as in the previous article, is to look at GithubApp, the injector:

//HasActivity Injector needs to be implemented
class GithubApp : Application(), HasActivityInjector {
    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
    
    override fun onCreate() {
       ……
       //Rewritten onCreate(), this sentence is the focus
       AppInjector.init(this)
    }
    override fun activityInjector() = dispatchingAndroidInjector
}

Step 2, Component:

@Component(modules = [AndroidInjectionModule::class])
interface AppComponent {
    //Builder is declared here
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        fun build(): AppComponent
    }
    fun inject(githubApp: GithubApp)
}

After the build, GithubApp_Members Injector and DaggerAppComponent come out
GithubApp is Application, so HasActivity Injector is implemented here, enabling Activity injection.
Here's another important class: Dispatching Android Injector. I will elaborate on that later.

AppInjector (1)

object AppInjector {
    fun init(githubApp: GithubApp) {
        //First sentence: Complete the injection of githubApp
        DaggerAppComponent.builder().application(githubApp).build().inject(githubApp)
        //Second sentence: override on Activity Created, add handleActivity
        githubApp.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
                override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {handleActivity(activity)}
        })
    }
    private fun handleActivity(activity: Activity)  {...}//To look good, put it away here first.
}

First look at the first sentence of AppInjector.init:

    DaggerAppComponent.builder()//Builder is obtained by this static method
 	                  .application(githubApp)//Builder with githubApp
 			          .build()//Get the DaggerAppComponent object
 			          .inject(githubApp)

The DaggerAppComponent methods used here are:

public final class DaggerAppComponent implements AppComponent {
  private DaggerAppComponent(Builder builder) {}

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

  private DispatchingAndroidInjector<Activity> getDispatchingAndroidInjectorOfActivity() {
    //Here a Dispatching Android Injector for an empty Map is returned
    return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(        
        Collections.<~> emptyMap());
  }

  //Inject Dispatching Android Injector into githubApp. dispatching Android Injector
  @Override
  public void inject(GithubApp githubApp) {injectGithubApp(githubApp);}

  private GithubApp injectGithubApp(GithubApp instance) {
    GithubApp_MembersInjector.injectDispatchingAndroidInjector(instance, getDispatchingAndroidInjectorOfActivity());
    return instance;
  }

  //The AppComponent.Builder implemented here is what was previously written in Component.
  //Rewritten application s, build s are our own definitions
  private static final class Builder implements AppComponent.Builder {
    private Application application;

    @Override
    public AppComponent build() {return new DaggerAppComponent(this);}

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

The code is a bit complicated, so here are only two things to remember:
1. The first sentence of AppInjector.init completes githubApp. dispatching Android Injector injection
2. Through GithubApp_Members Injector. (The code is not difficult, I talked about it in the last part.)

Here comes the Dispatching Android Injector. Before continuing with the second sentence of AppInjector.init, take a look at it first.

DispatchingAndroidInjector

First look at Dispatching Android Injector

/*
 * Examples of core Android types (Activity, Fragment... ) Perform injection, which is implemented by the Android framework rather than Dagger
 * By implementing the Android Injector method, the Android Injector. Factory injected into the class is put into the Map.
 * By calling Object#getClass() on the instance, you can get its Android Injector. Factory.
 */
@Beta
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
  /*Injecting Map type of Factory, the following code omits Map type for convenience
  *In this Map, K is Class and V is Android Injector.Factory.
  */
  private final Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories;

  @Inject
  DispatchingAndroidInjector(Map<~> injectorFactories) {
  	this.injectorFactories = injectorFactories;
  }

  //Attempt to inject on {instance} to return success
  @CanIgnoreReturnValue
  //Can you get factory in Map?
  public boolean maybeInject(T instance) {
    Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
        injectorFactories.get(instance.getClass());
    if (factoryProvider == null) {
      return false;
    }

    @SuppressWarnings("unchecked")
    AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
    //Post-transition Implementation Injection
    try {
      AndroidInjector<T> injector =checkNotNull(factory.create(instance),, );

      injector.inject(instance);
      return true;
    } catch (ClassCastException e) {}
  }

  //Injection of {instance} Executive Members
  @Override
  public void inject(T instance) {
    boolean wasInjected = maybeInject(instance);
  }
}

By looking at the core code of Dispatching Android Injector, it is mainly through:
Android Injector. create () and Android Injector. inject () complete the injection.

It's a bit messy here, remember Dispatching Android Injector and AppInjector will be clear later.

Activity injection

Let's complete MainActivity joining Dispatching Android Injector (I'm going to do the sample code step by step)
MainActivity is like this:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
    }
}

Or the general rule, Module:

@Suppress("unused")
@Module
abstract class MainActivityModule {
    @ContributesAndroidInjector
    abstract fun contributeMainActivity(): MainActivity
}

OK, build a look at Dagger App Component

public final class DaggerAppComponent implements AppComponent {
  //Provider of MainActivity
  private Provider<MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.Builder>
      mainActivitySubcomponentBuilderProvider;

  //Constructor to perform initialization
  private DaggerAppComponent(Builder builder) {initialize(builder);}
  private void initialize(final Builder builder) {
    //Initialize Provider for MainActivity
    this.mainActivitySubcomponentBuilderProvider =
        new Provider<MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.Builder>() {
          @Override
          public MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.Builder get() {
            return new MainActivitySubcomponentBuilder();
          }
        };
  }
  public static AppComponent.Builder builder() {return new Builder();}

  //Get Map with MainActivity
  private Map<~> getMapOfClassOfAndProviderOfFactoryOf() {
    return Collections.<~>
    	 singletonMap(MainActivity.class, (Provider) mainActivitySubcomponentBuilderProvider);
  }

  //Create and return Map's Dispatching Android Injector object with MainActivity
  private DispatchingAndroidInjector<Activity> getDispatchingAndroidInjectorOfActivity() {
    return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(
        getMapOfClassOfAndProviderOfFactoryOf());
  }


  //Execution injection
  @Override
  public void inject(GithubApp githubApp) {injectGithubApp(githubApp);}
  private GithubApp injectGithubApp(GithubApp instance) {
    GithubApp_MembersInjector.injectDispatchingAndroidInjector(
        instance, getDispatchingAndroidInjectorOfActivity());
    return instance;
  }

  //The code here is omitted, which was analyzed earlier.
  private static final class Builder implements AppComponent.Builder {...}

  private final class MainActivitySubcomponentBuilder
      extends MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.Builder {
    private MainActivity seedInstance;

    @Override//The override here is Android Injector. Builder. build ()
    public MainActivityModule_ContributeMainActivity.MainActivitySubcomponent build() {
      if (seedInstance == null) {
        throw new IllegalStateException(MainActivity.class.getCanonicalName() + " must be set");
      }
      return new MainActivitySubcomponentImpl(this);
    }

    @Override//Override here is Android Injector. Builder. seedInstance ()
    public void seedInstance(MainActivity arg0) {
      this.seedInstance = Preconditions.checkNotNull(arg0);
    }
  }

  private final class MainActivitySubcomponentImpl
      implements MainActivityModule_ContributeMainActivity.MainActivitySubcomponent {
    private MainActivitySubcomponentImpl(MainActivitySubcomponentBuilder builder) {}

    @Override//Here is Android Injector. inject ()
    public void inject(MainActivity arg0) {}
  }
}

There's a lot of code. Simply put, MainActivity is injected through Dispatching Android Injector.

Add an Activity

We need to have an activity first. It's called Activity 2.
Add:

@Suppress("unused")
@Module
abstract class MainActivityModule {
    @ContributesAndroidInjector
    abstract fun contributeMainActivity(): MainActivity
    @ContributesAndroidInjector
    abstract fun contributeActivity2(): Activity2
}

After build ing, look at Dagger App Component

public final class DaggerAppComponent implements AppComponent {
  private Provider<MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.Builder>
      mainActivitySubcomponentBuilderProvider;

  //More Activity 2
  private Provider<MainActivityModule_ContributeActivity2.Activity2Subcomponent.Builder>
      activity2SubcomponentBuilderProvider;

  //That's it. Two are added to the Map.
  private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
      getMapOfClassOfAndProviderOfFactoryOf() {
    return MapBuilder
        .<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
            newMapBuilder(2)
        .put(MainActivity.class, (Provider) mainActivitySubcomponentBuilderProvider)
        .put(Activity2.class, (Provider) activity2SubcomponentBuilderProvider)
        .build();
  }

  //Here are also two sets.
  private void initialize(final Builder builder) {
    this.mainActivitySubcomponentBuilderProvider =...
    this.activity2SubcomponentBuilderProvider =...
  }

   private final class MainActivitySubcomponentBuilder
      extends MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.Builder {...}
   
  private final class MainActivitySubcomponentImpl
      implements MainActivityModule_ContributeMainActivity.MainActivitySubcomponent {...}

  private final class Activity2SubcomponentBuilder
      extends MainActivityModule_ContributeActivity2.Activity2Subcomponent.Builder {...}

  private final class Activity2SubcomponentImpl
      implements MainActivityModule_ContributeActivity2.Activity2Subcomponent {...}
}

It's double...

Sum up

  • Actual projects inject Activity, just modify the Module. Simple don't want it.
  • By looking at the source code, we can better understand the application of builder and factory mode, and understand the three implementation methods of Map.
  • Why inject Activity? Continue to find the benefits of doing so.

Posted by macattack on Fri, 25 Jan 2019 05:03:13 -0800