DaggerAndroid 使用及原理

已經有了 Dagger 2 爲什麼還要使用 DaggerAndroid 呢?關於這一點在谷歌的官方說明文檔:https://google.github.io/dagger//android.html 中已經有了明確的解釋。DaggerAndroid 是谷歌基於 Dagger 2 的一個擴展庫,更適合在 Android 開發中使用。爲了更好的學習 DaggerAndroid,還不瞭解 Dagger 2 的同學建議先學習 Dagger 2:Dagger 2 使用及原理

一、更好的 DaggerAndroid

Dagger 2 雖然優秀,但在 Android 開發中確實會存在一些問題,回顧之前 Dagger 2 的使用,我們必須在 Activity 或 Fragment 的聲明週期方法中添加類似這樣的代碼來完成依賴對象的創建和注入:

DaggerMainComponent.builder()
                .mainModule(new MainModule())
                .build()
                .inject(this);

咋一看可能覺得沒什麼問題,但如果在幾十個甚至幾百個 Activity 或 Fragment 中都添加類似的代碼,這對後期的會維護、擴展還是會帶來一定困難的;這些模板代碼都大同小異,可能都是複製、粘貼,然後簡單的修改下,這並不是一種好的做法,而且無法將其轉移到基類裏,我們更想看到的是每個 Activity 或 Fragment 中不出現類似的配置代碼,即我不關心框架是如何給我注入依賴對象的,減少模板代碼。

爲了解決這樣的問題,就有必要使用 DaggerAndroid。

二、基本使用

1、添加依賴庫

implementation 'com.google.dagger:dagger-android:2.15'
// 如果使用了 Android support libraries 中的相關組件,則需要添加該依賴
implementation 'com.google.dagger:dagger-android-support:2.15'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.15'
// Dagger 2 的編譯時註解處理器(必須)
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'

添加了依賴庫後,接下來我們以 MainActivity 的依賴對象注入爲例。

2、編寫 Subcomponent + Module

首先編寫 MainActivity 對應的 MainActivitySubcomponent 接口,注意泛型參數類型爲要注入的的 MainActivity 類型,同時裏邊的 Builder 抽象類不可缺少:

@Subcomponent(modules = {AndroidInjectionModule.class})
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {

    }
}

創建 MainActivity 對應的 MainActivityModule 類,@Module註解的subcomponents屬性值爲前邊創建的 MainActivitySubcomponent 類型,@ActivityKey註解的屬性值爲需要依賴注入的 MainActivity 類型,同時可以在 MainActivityModule 中定義提供依賴對象的方法,bindMainActivityInjectorFactory()方法的參數類型就是 MainActivitySubcomponent 內部 Builder 類:

@Module(subcomponents = {MainActivitySubcomponent.class})
public abstract class MainActivityModule {
    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);

    @Provides
    static FlowerBean provideFlower() {
        return new FlowerBean("rose", "red");
    }
}

3、編寫全局的 Component

這個 AppComponent 會在自定義 Application 中用到,注意modules屬性的參數,AndroidInjectionModule主要提供 DaggerAndroid 核心組件,必須配置,如果使用了 Android support libraries 則需要配置AndroidSupportInjectionModuleMainActivityModule則是我們自定義的,提供執行的依賴對象:

@Component(modules = {
        AndroidInjectionModule.class,
        AndroidSupportInjectionModule.class,
        MainActivityModule.class
})
public interface AppComponent {
    void inject(App app);
}

4、自定義 Application

這是關鍵的一步,自定義 Application 實現 HasActivityInjector 接口,實現activityInjector()接口,返回值爲由框架負責注入的dispatchingActivityInjector對象,底層通過DaggerAppComponent.create().inject(this)會完成dispatchingActivityInjector的依賴注入,這個也是依賴注入的準備階段,必不可少。注意DaggerAppComponent是項目編譯後纔會有的。

dispatchingActivityInjector是什麼,有什麼用呢?不看編譯後生成的代碼是不知道的,這個後邊說。

public class App extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.create().inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }
}

5、編寫 MainActivity

聲明需要依賴注入的 flower 對象,這個和 Dagger 2 是一樣的,然後完成調用AndroidInjection.inject(this)會在底層完成對象的創建和注入。
注意AndroidInjection.inject(this)需要寫在super.onCreate(savedInstanceState)之前。

public class MainActivity extends AppCompatActivity {
    @Inject
    FlowerBean flower;

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

        Log.e("flower", flower.toString());
    }
}

在 Fragment 中的使用類似,有興趣的可以參考 demo,鏈接會在文末給出,或參考官方的例子。

三、原理

上邊我在結合官方示例的基礎上,實現了依賴注入。但是整個過程有各種疑問,這個類有啥用,爲啥這樣寫,它們之間有什麼依賴關係。如果看編譯時生成的代碼,肯定一臉懵逼,只能照貓畫虎的寫代碼,出現錯誤都不好找原因。如果不懂原理,真的很難靈活的運用它,這也是 Dagger 2 、DaggerAndroid 比較難上手的原因吧。

接下來我們來分析 DaggerAndroid 的依賴注入原理,看一看上邊例子的各個部分是如何一起工作的。

先回顧一下 Dagger 2 的底層原理,當項目編譯時會生成對應的輔助代碼,然後通過類似文章開頭的模板代碼進一步的使用生成的輔助類完成依賴對象的創建和注入。

DaggerAndroid 同樣也是在編譯時生成對應的輔助代碼,但之後需要先在自定義 Application 中通過DaggerAppComponent.create().inject(this)實現dispatchingActivityInjector的依賴注入,以保證activityInjector()方法的返回值有效,這個返回值是依賴注入的關鍵,這也算是依賴注入的準備階段,因爲當我們在 MainActivity 中執行AndroidInjection.inject(this)方法時需要間接的調用activityInjector()方法,最終來完成依賴對象的創建和注入,所以我們分兩部分來看。

項目編譯後生成的輔助代碼如下:


1、準備階段

首先看 Application 中的DaggerAppComponent.create().inject(this)是如何完成dispatchingActivityInjector的創建和注入。DaggerAppComponent生成類的源碼直達:DaggerAppComponent

跟隨create()方法逐步的看,首先是通過Builder內部類構建一個 DaggerAppComponent 對象:

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

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

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

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

  public static final class Builder {
    private Builder() {}

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

DaggerAppComponent 的私有構造函數中調用了initialize()爲 DaggerAppComponent 的mainActivitySubcomponentBuilderProvider變成變量賦值:

private void initialize(final Builder builder) {
    this.mainActivitySubcomponentBuilderProvider =
        new Provider<MainActivitySubcomponent.Builder>() {
          @Override
          public MainActivitySubcomponent.Builder get() {
            return new MainActivitySubcomponentBuilder();
          }
        };
  }

mainActivitySubcomponentBuilderProviderProvider類型,通過get()方法可以返回其保存的MainActivitySubcomponentBuilder對象的值。繼續看 MainActivitySubcomponentBuilder 的實現:

private final class MainActivitySubcomponentBuilder extends MainActivitySubcomponent.Builder {
    ......
    @Override
    public MainActivitySubcomponent build() {
      ......
      return new MainActivitySubcomponentImpl(this);
    }
    ......
  }

MainActivitySubcomponentBuilder 就是我們前邊例子(二、2)中接口 MainActivitySubcomponent 內部 Builder 類的子類,重寫了build()方法並返回了一個 MainActivitySubcomponentImpl 對象:

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

    @Override
    public void inject(MainActivity arg0) {
      injectMainActivity(arg0);
    }

    private MainActivity injectMainActivity(MainActivity instance) {
      MainActivity_MembersInjector.injectFlower(
             instance, MainActivityModule_ProvideFlowerFactory.proxyProvideFlower());
      return instance;
    }
  }

這個類實現了我們自定義的 MainActivitySubcomponent 接口,並重寫了其父類AndroidInjectorinject()方法,在injectMainActivity()中,MainActivityModule_ProvideFlowerFactory.proxyProvideFlower()會創建一個 Flower 對象,MainActivity_MembersInjector.injectFlower()方法則會將創建好的 Flower 對象賦值給 MainActivity 中的flower變量,即依賴注入。

再回到前邊的initialize()方法,所以mainActivitySubcomponentBuilderProvider中最終保存了一個MainActivitySubcomponentBuilder對象,通過該對象可以調用MainActivitySubcomponentImpl對象的inject()方法完成依賴對象的注入。

DaggerAppComponent.create()的核心內容就這些了,然後看其inject(this)方法:

public final class DaggerAppComponent implements AppComponent {

  @Override
  public void inject(App app) {
    injectApp(app);
  }

  private App injectApp(App instance) {
    App_MembersInjector.injectDispatchingActivityInjector(
        instance, getDispatchingAndroidInjectorOfActivity());
    return instance;
  }
}

DaggerAppComponent 實現了前邊例子(二、3)中的 AppComponent 接口,並重寫了inject()方法。其中核心方法就是 App_MembersInjectorinjectDispatchingActivityInjector()方法:

public static void injectDispatchingActivityInjector(
      App instance, DispatchingAndroidInjector<Activity> dispatchingActivityInjector) {
    instance.dispatchingActivityInjector = dispatchingActivityInjector;
  }

即給自定義 Application 中的 dispatchingActivityInjector成員變量賦值,那這個值是如何被創建的呢,我們看getDispatchingAndroidInjectorOfActivity()方法:

 private DispatchingAndroidInjector<Activity> getDispatchingAndroidInjectorOfActivity() {
    return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(
        getMapOfClassOfAndProviderOfFactoryOf());
  }

先看裏邊的getMapOfClassOfAndProviderOfFactoryOf()方法:

private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
      getMapOfClassOfAndProviderOfFactoryOf() {
    return Collections
        .<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
            singletonMap(MainActivity.class, (Provider) mainActivitySubcomponentBuilderProvider);
  }

就是返回了一個 key 爲MainActivity.class,value 爲 mainActivitySubcomponentBuilderProvider的 Map,那麼上邊DispatchingAndroidInjector_Factory類的newDispatchingAndroidInjector()方法呢:

public static <T> DispatchingAndroidInjector<T> newDispatchingAndroidInjector(
      Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) {
    return new DispatchingAndroidInjector<T>(injectorFactories);
  }

返回了一個DispatchingAndroidInjector類的對象,所以 DispatchingAndroidInjector 對象中就保存了需要依賴注入的 MainActivity 類型,以及可以實現 MainActivity 依賴注入的mainActivitySubcomponentBuilderProvider對象的Map,所以我們 Application 中的 dispatchingActivityInjector成員變量也就有了同樣的信息。

到這裏依賴注入的準備階段就結束了。

2、依賴注入階段

依賴注入階段就是通過 MainActivity 中的AndroidInjection.inject(this)完成的,我們來看inject(this)的實現:

public static void inject(Activity activity) {
    checkNotNull(activity, "activity");
    Application application = activity.getApplication();
    if (!(application instanceof HasActivityInjector)) {
      throw new RuntimeException(
          String.format(
              "%s does not implement %s",
              application.getClass().getCanonicalName(),
              HasActivityInjector.class.getCanonicalName()));
    }

    AndroidInjector<Activity> activityInjector =
        ((HasActivityInjector) application).activityInjector();
    checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());

    activityInjector.inject(activity);
  }

即先得到當前 Activity 的 Application,並且 Application 需要實現HasActivityInjector接口,我們的自定義 Application 符合這樣的條件。然後調用 Application 中重寫的 activityInjector()方法,所以activityInjector.inject(activity)就是通過我們 Application 中的dispatchingActivityInjector對象的inject()方法,即DispatchingAndroidInjector類的inject()方法:

public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
  @Override
  public void inject(T instance) {
    boolean wasInjected = maybeInject(instance);
    if (!wasInjected) {
      throw new IllegalArgumentException(errorMessageSuggestions(instance));
    }
  }
}

間接調用了maybeInject()

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();
    try {
      AndroidInjector<T> injector =
          checkNotNull(
              factory.create(instance), "%s.create(I) should not return null.", factory.getClass());

      injector.inject(instance);
      return true;
    } catch (ClassCastException e) {
      throw new InvalidInjectorBindingException(
          String.format(
              "%s does not implement AndroidInjector.Factory<%s>",
              factory.getClass().getCanonicalName(), instance.getClass().getCanonicalName()),
          e);
    }
  }

先從準備階段保存的Map中取出 MainActivity 對應的 mainActivitySubcomponentBuilderProvider對象,然後取出裏邊的值,即 MainActivitySubcomponentBuilder對象,它實現了AndroidInjector.Factory<T>接口,我們簡化一下try-catch裏邊的代碼:

 AndroidInjector<T> injector = factory.create(instance);
 injector.inject(instance);

factory.create(instance)最終會調用MainActivitySubcomponentBuilderbuild()方法來創建MainActivitySubcomponentImpl對象,所以injector就是一個MainActivitySubcomponentImpl對象,所以通過injector.inject(instance)就調用了前邊準備階段MainActivitySubcomponentImpl實現類中的inject(MainActivity arg0)方法,最終完成了 MainActivity 中依賴對象的創建和注入。

四、小結

DaggerAndroid 相比 Dagger 2 的底層依賴注入原理要複雜一些,你寫的代碼越少意味着框架要幫你做的事情越多。通過閱讀源碼,可以更好的理解我們使用框架的每一步,框架底層是如何工作的,而不是簡單的使用。

但是不得不說,DaggerAndroid 的使用過程依舊比較繁瑣,還是會讓很多人望而卻步的,如果你用 Kotlin 語言開發 Android 的話,那麼有更好用、更簡單的依賴注入框架可以選擇,那就是 Kodein-DI

示例代碼在 https://github.com/SheHuan/Dagger2Demo 的 dagger-android 分支上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章