Dagger2深刻理解

最近,看到一些小夥伴想要入門Dagger2,加之最近剛經歷了Dagger2的水深火熱,在這裏針對Dagger2中不一樣的註解方式,會生成怎樣的代碼,結合其生成的不一樣代碼,來幫助你們作一些深刻的理解。java

概念

首先,Dagger2是一個DI的解決方案,跟以前接觸過的Spring相比,它最主要的好處是經過apt插件在編譯階段時,用來生成注入的代碼;而Spring是須要在運行時,經過XML或者註解來進行代碼注入的。因此,相比來講,在性能上,Dagger2是優於Spring,但帶來的是編譯階段時間的延長。這樣的話,每當咱們修改或者添加這些註解代碼的時候,就須要咱們從新Build一下(即由apt插件來生成咱們所須要使用的代碼),build時間的延長,感受這對Android開發程序員來講,應該習覺得常了吧。(掩面而泣。。。)android

另外,不得不提的就是apt插件的成熟。apt插件經過生成代碼的方式會使得咱們則針對特定規則的代碼,經過添加註解,使用apt在編譯階段生成代碼,減小咱們的代碼書寫量。在gayhub上,已經有不少成熟的庫,在使用apt來生成代碼,像bundler這個庫,就是經過封裝Intent參數跟界面組件來綁定,這樣咱們就可沒必要經過getIntent來一個個獲取參數。另外還提供了使用saveStaterestoreState,使得咱們在界面組件異常退出的時候,沒必要再使用savedInstance來進行數據的保存與獲取。還有就是支持Parcelable數據以及自定義數據parser,另做者不禁不喜歡啊。git

使用

根項目添加apt的版本依賴程序員

dependencies {
    ...
   classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    ...
}

項目中配置apt插件的使用,以及Dagger2的版本github

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
   compile 'com.google.dagger:dagger:2.0.2'
   compile 'com.google.dagger:dagger-compiler:2.0.2'
   compile 'org.glassfish:javax.annotation:10.0-b28'
}

重要概念

這裏簡單提一下它們的主要做用,dagger2兩個很重要的東西,不過介紹的文章不少,須要的話,可自行查找一下。服務器

  • Component

    用來爲「被注入方「提供其所須要的注入類。當在子Scoped的Component中,也要被注入相同的類的時候,則須要在當前的Component添加相應的返回對應的類的方法。app

  • Module

    用來提供上層所須要的依賴類。這裏所帶來的好處就是,全部的依賴類都是經過Module來提供出去,當咱們的依賴發生改變時,咱們只須要在這裏改變提供一個新的對象類便可,而不影響咱們上層使用的代碼。這裏,舉一個最簡單的例子就是,當咱們調用服務器提供的API的時候,咱們爲上層提供了一個Repository命名的接口,並對其返回的是調用API的類;這裏,當API還未實現,咱們僅僅對其提供一個MockAPI便可,上層的調用者根本不須要關心個人數據提供從哪裏來,是否真實。ide

依賴實現輔助類

  • Provider

    Provider定義了一個來提供泛型T的方式,是個很簡單的接口。性能

public interface Provider<T> {

   /**
    * Provides a fully-constructed and injected instance of {@code T}.
    *
    * @throws RuntimeException if the injector encounters an error while
    *  providing an instance. For example, if an injectable member on
    *  {@code T} throws an exception, the injector may wrap the exception
    *  and throw it to the caller of {@code get()}. Callers should not try
    *  to handle such exceptions as the behavior may vary across injector
    *  implementations and even different configurations of the same injector.
    */
   T get();
}
  • ScopedProvider

    ScopedProviderProvider的基礎上,加上了Scoped的概念,主要經過指定類型的Factory來建立出來對應類型的ScopedProvider,在get方法上,主要經過double-checked來確保獲取實例T的惟一性。gradle

/**
 * A {@link Provider} implementation that memoizes the result of a {@link Factory} instance.
 *
 * @author Gregory Kick
 * @since 2.0
 */
public final class ScopedProvider<T> implements Provider<T> {
   private static final Object UNINITIALIZED = new Object();

   private final Factory<T> factory;
   private volatile Object instance = UNINITIALIZED;

   private ScopedProvider(Factory<T> factory) {
      assert factory != null;
      this.factory = factory;
   }

   @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
      @Override
      public T get() {
         // double-check idiom from EJ2: Item 71
         Object result = instance;
         if (result == UNINITIALIZED) {
            synchronized (this) {
               result = instance;
               if (result == UNINITIALIZED) {
                  instance = result = factory.get();
               }
            }
         }
         return (T) result;
      }

   /** Returns a new scoped provider for the given factory. */
   public static <T> Provider<T> create(Factory<T> factory) {
      if (factory == null) {
         throw new NullPointerException();
      }
      return new ScopedProvider<T>(factory);
   }
}
  • Factory

    Factory僅僅繼承了一個Provider,並是一個空的實現。

public interface Factory<T> extends Provider<T> {}
  • MembersInjector

    從類的註釋中,能夠看出它的主要做用是給類的屬性字段,或者方法參數來提供注入,並忽略是否在含有構造器的注入,都會生成MembersInjector的類。

/**
 * Injects dependencies into the fields and methods on instances of type {@code T}. Ignores the
 * presence or absence of an injectable constructor.
 *
 * @param <T> type to inject members of
 *
 * @author Bob Lee
 * @author Jesse Wilson
 * @since 2.0 (since 1.0 without the provision that {@link #injectMembers} cannot accept
 *      {@code null})
 */
public interface MembersInjector<T> {

   /**
    * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
    * absence of an injectable constructor.
    *
    * <p>Whenever the object graph creates an instance, it performs this injection automatically
    * (after first performing constructor injection), so if you're able to let the object graph
    * create all your objects for you, you'll never need to use this method.
    *
    * @param instance into which members are to be injected
    * @throws NullPointerException if {@code instance} is {@code null}
    */
   void injectMembers(T instance);
}

注入方式

這裏經過列舉幾種不一樣的注入方式,探究其的實現,瞭解它們的不一樣使用場合

  • 構造器注入

    這裏先看一個簡單的類

public class TestData {

   @Inject
   public TestData() {
   }
}

經過在構造器上添加@Inject註解,則會編譯生成一個實現了Factory接口的單例類,用來生成TestData類。生成的代碼以下:

@Generated("dagger.internal.codegen.ComponentProcessor")
public enum TestData_Factory implements Factory<TestData> {
   INSTANCE;

   @Override
   public TestData get() {  
      return new TestData()
   }

   public static Factory<TestData> create() {  
      return INSTANCE;
   }
}

能夠看出經過構造器注入的生成的Factory類,是一個經過enum來實現的一個單例工廠類,是否是有個新技能Get。

  • Module注入

    實現一個Module來提供TestData的依賴,代碼以下:

@Module
public class TestModule {

   @Singleton
   @Provides
   public TestData provideTestData(){
      return new TestData();
   }
}

再查看一下,經過apt生成以後的代碼:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class TestModule_ProvideTestDataFactory implements Factory<TestData> {
   private final TestModule module;

   public TestModule_ProvideTestDataFactory(TestModule module) {  
      assert module != null;
      this.module = module;
   }

   @Override
      public TestData get() {  
         TestData provided = module.provideTestData();
         if (provided == null) {
            throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
         }
         return provided;
      }

   public static Factory<TestData> create(TestModule module) {  
      return new TestModule_ProvideTestDataFactory(module);
   }
}

能夠看出,針對Module註解,dagger2會根據我們定義了Provides的註解方法,會生成相應以Module爲開頭的Factory類,而這個Factory會以Module做爲參數,經過調用Module中的代碼,來實現注入類的提供。

  • 屬性注入,方法注入

    這裏,以咱們經常使用的Activity爲例,畢竟Activity的使用但是不容許咱們經過構造器來生成一個Activity的,此時就是MembersInjector的用武之地了。

來看個Activity的代碼先。

public class MainActivity extends AppCompatActivity {

   @Inject
   TestData mData;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
   }

}

固然,也可使用方法來進行注入,像下面這樣:

@Inject
public void setTestData(TestData testData){
}

會生成一個MainActivity_MembersInjector的類,以下:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
   private final MembersInjector<AppCompatActivity> supertypeInjector;
   private final Provider<TestData> mDataProvider;

   public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<TestData> mDataProvider) {  
      assert supertypeInjector != null;
      this.supertypeInjector = supertypeInjector;
      assert mDataProvider != null;
      this.mDataProvider = mDataProvider;
   }

   @Override
      public void injectMembers(MainActivity instance) {  
         if (instance == null) {
            throw new NullPointerException("Cannot inject members into a null reference");
         }
         supertypeInjector.injectMembers(instance);
         instance.mData = mDataProvider.get();
      }

   public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<TestData> mDataProvider) {  
      return new MainActivity_MembersInjector(supertypeInjector, mDataProvider);
   }
}

從上方能夠看出,MainActivity_MembersInjector類經過實現MembersInjector類,經過injectMembers方法,來給MainActivity的實例,採用Provider獲取實例的方式,來給咱們以前定義的Inject參數或者屬性,來賦值相應的注入類。
另外,咱們看到,在injectMembers的方法中,也會調用父類的supertypepInjector的類,來調用父類的參數注入。

Component完成注入

上面,咱們提到了Activity所須要的類注入,只能經過方法參數,或者字段屬性兩個方式,(構造器是行不通的)。其MembersInjector的類頁編寫好了,咱們該怎麼調用呢?繼續上面的例子,咱們來給MainActivity來提供注入方式,神奇的Component就登上舞臺了。

@Singleton
@Component(modules = { TestModule.class })
public interface ApplicationComponent {
   void inject(MainActivity mainActivity);

}

這裏,component通常在使用的時候,都是須要跟Scope相綁定的,否則會報錯。看它的生成代碼:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerApplicationComponent implements ApplicationComponent {
   private Provider<TestData> provideTestDataProvider;
   private MembersInjector<MainActivity> mainActivityMembersInjector;

   private DaggerApplicationComponent(Builder builder) {  
      assert builder != null;
      initialize(builder);
   }

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

   public static ApplicationComponent create() {  
      return builder().build();
   }

   private void initialize(final Builder builder) {  
      this.provideTestDataProvider = ScopedProvider.create(TestModule_ProvideTestDataFactory.create(builder.testModule));
      this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideTestDataProvider);
   }

   @Override
      public void inject(MainActivity mainActivity) {  
         mainActivityMembersInjector.injectMembers(mainActivity);
      }

   public static final class Builder {
      private TestModule testModule;

      private Builder() {  
      }

      public ApplicationComponent build() {  
         if (testModule == null) {
            this.testModule = new TestModule();
         }
         return new DaggerApplicationComponent(this);
      }

      public Builder testModule(TestModule testModule) {  
         if (testModule == null) {
            throw new NullPointerException("testModule");
         }
         this.testModule = testModule;
         return this;
      }
   }
}

咱們定義好的Component接口,都會生成一個以Dagger開頭的一個實現類。在其中,就能夠看到它在實現我們定義的void inject(MainActivity activity)方法,就是經過使用MainActivity_MembersInjector類,來完成這一步注入的,有木有頗感神奇啊。這就是apt插件的神奇之處,定義好了規則,按照規則來生成代碼便可。

這裏,緊接着說起一下Scope的的用法。當咱們爲Component定義了Scope以後,而且在module的方法上,添加了Scope註解,這樣Dagger2在Component中生成相應的Provider的時候,就會在前面添加ScopedProvider,來對咱們所須要的Provider來提供單例模式的訪問。具體能夠看到的代碼就是上面初始化過程當中,生成provideTestDataProvider的字段。

總結

掌握了上面說起的這些內容,則Dagger2的不一樣注入方法,生成怎樣的代碼,咱們就上手了Dagger2的使用,如果在使用中還遇到問題的話,歡迎加入QQ羣:289926871,來進行交流。

PS: 版權歸做者全部,轉載請註明原文連接

相關文章
相關標籤/搜索