Dagger2詳解-從代碼分析其原理

Dagger2基本概念請移步官方文檔,或者我的翻譯

架構方面請關注GitHub(MVP+Retrofit+Dagger2+Okhttp)及我的文章Android UI框架快速搭建實踐

Dagger2是Dagger1的分支,由谷歌公司接手開發,目前的版本是2.0。Dagger2解決問題的基本思想是:利用生成和寫的代碼混合達到看似所有的產生和提供依賴的代碼都是手寫的樣子。

如果我們將Dagger2和1比較,他們兩個在很多方面都非常相似,但也有很重要的區別,如下:

  1. 再也沒有使用反射:圖的驗證、配置和預先設置都在編譯的時候執行。
  1. 容易調試和可跟蹤:完全具體地調用提供和創建的堆棧
  2. 更好的性能:谷歌聲稱他們提高了13%的處理性能
  3. 代碼混淆:使用派遣方法,就如同自己寫的代碼一樣

當然所有這些很棒的特點都需要付出一個代價,那就是缺乏靈活性,例如:Dagger2沒用反射所以沒有動態機制。

所以在Android UI框架快速搭建實踐這篇文章中,因為我將Presenter作為泛型成員變量抽取到BaseFragment中了,所以View層的Presenter無法使用Dagger2實現注入。如果要實現Presenter注入,則需要在每個View實現中注入對應的Presenter實例,這樣就無法抽取到基類中了。這個在具體實踐中,需要你自己權衡。

本文就GitHub項目,從生成的代碼對Dagger2進行分析。

使用Dagger2,在module的gradle中配置:

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

android{
    ...
}
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        //添加apt插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    ...
 }

使用時,定義好component及module后,build工程,Dagger2編譯器就會生成對應代碼,然后在需要注入的地方調用生成的component實現,名為Dagger前綴+component名稱的類完成綁定。

先看結構圖:

dagger2.png

大概介紹一下基本概念:

  • Module:使用@Module注解,封裝@Provides方法,@Provides注解方法提供依賴對象。

  • @Inject:在需要依賴注入的地方使用。Dagger2提供3種方式:

    • 構造方法注入:在類的構造方法前面注釋@Inject
    • 成員變量注入:在類的成員變量(非私有)前面注釋@Inject
    • 函數方法注入:在函數前面注釋@Inject
  • @Peractivity:自定義scope注解,約束依賴對象的生命周期。

  • @Singleton:api提供的scope注解,保證對象在對象圖中唯一。

  • Component:使用@Component注解的接口,是@Inject和@Module聯系的橋梁,子component可以使用@Subcomponent也可以指定@Component的dependency參數。

是時候上代碼了(Dagger2生成的代碼在build/generated/source/apt/目錄下):

DemoApplicationModule的定義:

@Module
public class DemoApplicationModule {
  private final Application application;

  public DemoApplicationModule(Application application) {
    this.application = application;
  }

  /**
   * Expose the application to the graph.
   */
  @Provides @Singleton Application application() {
    return application;
  }

  @Provides @Singleton LocationManager provideLocationManager() {
    return (LocationManager) application.getSystemService(LOCATION_SERVICE);
  }
}

來看生成的代碼:

public final class DemoApplicationModule_ApplicationFactory implements Factory<Application> {
  private final DemoApplicationModule module;

  public DemoApplicationModule_ApplicationFactory(DemoApplicationModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Application get() {
    return Preconditions.checkNotNull(
        module.application(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Application> create(DemoApplicationModule module) {
    return new DemoApplicationModule_ApplicationFactory(module);
  }
}   
----------------
public final class DemoApplicationModule_ProvideLocationManagerFactory
    implements Factory<LocationManager> {
  private final DemoApplicationModule module;

  public DemoApplicationModule_ProvideLocationManagerFactory(DemoApplicationModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public LocationManager get() {
    return Preconditions.checkNotNull(
        module.provideLocationManager(),
        "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<LocationManager> create(DemoApplicationModule module) {
    return new DemoApplicationModule_ProvideLocationManagerFactory(module);
  }
}

module中定義的@Provides方法會生成對應的工廠類,實現Factory<T>接口,復寫get()方法,get()中實際調用了module的provide方法,那module實例從哪里來的呢,看工廠類的創建方法中,看來這個module對象是需要外部傳入的。

ApplicationComponent定義:

@Singleton  
@Component(modules = DemoApplicationModule.class)
public interface ApplicationComponent {
  // Field injections of any dependencies of the DemoApplication
  void inject(DemoApplication application);

  // Exported for child-components.
  Application application();
  LocationManager locationManager();
}

生成的代碼:

public final class DaggerApplicationComponent implements ApplicationComponent {
  private Provider<LocationManager> provideLocationManagerProvider;

  private MembersInjector<DemoApplication> demoApplicationMembersInjector;

  private Provider<Application> applicationProvider;

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

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideLocationManagerProvider =
        DoubleCheck.provider(
            DemoApplicationModule_ProvideLocationManagerFactory.create(
                builder.demoApplicationModule));

    this.demoApplicationMembersInjector =
        DemoApplication_MembersInjector.create(provideLocationManagerProvider);

    this.applicationProvider =
        DoubleCheck.provider(
            DemoApplicationModule_ApplicationFactory.create(builder.demoApplicationModule));
  }

  @Override
  public void inject(DemoApplication application) {
    demoApplicationMembersInjector.injectMembers(application);
  }

  @Override
  public Application application() {
    return applicationProvider.get();
  }

  @Override
  public LocationManager locationManager() {
    return provideLocationManagerProvider.get();
  }

  public static final class Builder {
    private DemoApplicationModule demoApplicationModule;

    private Builder() {}

    public ApplicationComponent build() {
      if (demoApplicationModule == null) {
        throw new IllegalStateException(
            DemoApplicationModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerApplicationComponent(this);
    }

    public Builder demoApplicationModule(DemoApplicationModule demoApplicationModule) {
      this.demoApplicationModule = Preconditions.checkNotNull(demoApplicationModule);
      return this;
    }
  }
}   

DaggerApplicationComponent內部生成了Builder類,通過Builder的build()可以得到DaggerApplicationComponent對象,但必須先傳入DemoApplicationModule對象,這是當然的,Component本來就只是一個橋梁而已,別忘了之前定義ApplicationComponent的注解@Component(modules = DemoApplicationModule.class),這樣就好理解了吧。

調用了Builder的build()后,會走DaggerApplicationComponent的構造器方法,這里調用了initialize(builder), initialize(builder)又干了什么呢?這里初始化了待注入的依賴對象locationManager和application,通過之前的工廠類的create()方法得到工廠類對象,工廠類是實現Factory<T>接口的,Factory<T>又是繼承Provider<T>的,所以這里相當于拿到的是封裝了不同對象的Provider實例, DoubleCheck.provider(provider)又干了什么呢?DoubleCheck也實現了Provider<T>,它的provider(provider)方法實際上返回了本身實例,實際上也是一個Provider<T>,但為什么要這么做呢?看代碼:

public static <T> Provider<T> provider(Provider<T> delegate) {
...    
    return new DoubleCheck<T>(delegate);
}
  
private DoubleCheck(Provider<T> provider) {
    assert provider != null;
    this.provider = provider;
}

public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = provider.get();
        provider = null;
        }
      }
    }
    return (T) result;
  }

provider(Provider<T> delegate)調用了構造器方法,構造器中將傳入的Provider對象保存起來了,調用get()時會調用保存的provider對象的get(),實際上就是調用工廠方法的get()拿到對象,這樣就實現了懶加載,在需要的時候調用get(),這時才會調用工廠方法的get(),因為真正創建對象的細節是封裝在工廠類的get()中的,同時,它會將得到的對象緩存起來,這樣下次調用就不需要再調用工廠類創建對象了。

再看注入的地方:

public class DemoApplication extends Application {
  private ApplicationComponent applicationComponent;

  @Inject LocationManager locationManager;  
  @Override public void onCreate() {
    super.onCreate();
    applicationComponent = DaggerApplicationComponent.builder()
        .demoApplicationModule(new DemoApplicationModule(this))
        .build();
  }

  public ApplicationComponent component() {
    return applicationComponent;
  }
}

這里需要注入locationMnager,我們再看根據@Inject生成的代碼:

public final class DemoApplication_MembersInjector implements MembersInjector<DemoApplication> {
  private final Provider<LocationManager> locationManagerProvider;

  public DemoApplication_MembersInjector(Provider<LocationManager> locationManagerProvider) {
    assert locationManagerProvider != null;
    this.locationManagerProvider = locationManagerProvider;
  }

  public static MembersInjector<DemoApplication> create(
      Provider<LocationManager> locationManagerProvider) {
    return new DemoApplication_MembersInjector(locationManagerProvider);
  }

  @Override
  public void injectMembers(DemoApplication instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.locationManager = locationManagerProvider.get();
  }

  public static void injectLocationManager(
      DemoApplication instance, Provider<LocationManager> locationManagerProvider) {
    instance.locationManager = locationManagerProvider.get();
  }
}

injectMembers(DemoApplication instance) 方法將locationManager對象賦值給DemoApplication對象,DemoApplication和locationManagerProvider從哪里來呢?上面DaggerApplicationComponent的initialize(builder)中實例化了DemoApplication_MembersInjector并傳入需要的參數。

DaggerApplicationComponent實現了ApplicationComponent,當然要復寫其方法。
在DemoApplication中調用DaggerApplicationComponent的inject()方法,然后就會調用DemoApplication_MembersInjector的injectMembers(DemoApplication instance),就實現了依賴注入。

那DaggerApplicationComponent的其他方法呢?比如locationManager(),application()等呢,這些方法是在定義在ApplicationComponent中的,子component是共享父component中的綁定的,那子component怎么拿到父component中的依賴對象呢?

 this.locationManagerProvider =
        new Factory<LocationManager>() {
          private final ApplicationComponent applicationComponent = builder.applicationComponent;

          @Override
          public LocationManager get() {
            return Preconditions.checkNotNull(
                applicationComponent.locationManager(),
                "Cannot return null from a non-@Nullable component method");
          }
        };

在DaggerHomeComponent的initialize(final Builder builder)中通過applicationComponent.locationManager,子component就也有locationManager實例了。

Activity的依賴注入分析也是類似的,通過源碼,我們可以清晰的跟蹤其調用流程。

That's all!希望能幫到你。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容