Dagger2與AndroidInjector

1.遇到的問題

相信使用過Dagger開發Android應用的小夥伴會知道(如果你還不是很瞭解Daager,可以先看我之前的一篇基本介紹:Dagger2使用攻略),我們會在ActivityFragment的生命週期方法中執行成員注入。比如這樣:

public class MyActivity extends Activity {
  @Inject 
  Test test;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    DaggerActivityComponent
                .builder()
                .appComponent(((MyApplication)this.getApplication()).getAppComponent())
                .build()
                .inject(this);
    test.getXX();
  }
}

但是有一些問題:

  1. 隨着越來越多的這樣代碼塊的複製粘貼,使得以後很難重構。

  2. 更重要的是,它要求注入類型( MyActivity )知道其注入器。 即使這是通過接口而不是具體類型完成的,它打破了依賴注入的核心原則:一個類不應該知道如何注入它。

那麼我們怎麼解決他呢?

2.解決問題

不用擔心,Dagger2在2.10版本爲我們提供瞭解決的方案。 並在2.11版本中增加了@ContributesAndroidInjector註解,使得我們的實現更加方便。具體變化可以查看這裏

1.配置

首先將以下內容添加到您的build.gradle中:(在原先Dagger2的基礎上添加)

dependencies {
  compile 'com.google.dagger:dagger-android:2.11'
  compile 'com.google.dagger:dagger-android-support:2.11'
  annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
}

2.實現

爲了更清楚的說明,我會舉一個簡單的例子使用新舊兩種寫法實現。

現在的場景是這樣的,我要在一個頁面中使用一個Login對象,如下:

public class Login {

    private String name ;
    private String password ;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

假設初始化name小明password******。我們使用Dagger進行依賴注入,實現UserModule

@Module
public class UserModule {

    @Provides
    Login provideXiaoMingUser() {
        Login xiaomin = new Login();
        xiaomin.setPassword("******");
        xiaomin.setName("小明");
        return xiaomin;
    }

}

相對應的UserComponent如下

@Component(modules = {UserModule.class})
public interface UserComponent {
    void inject(SecondActivity mActivity);
}

那麼對應的SecondActivity也就出來了。

public class SecondActivity extends AppCompatActivity {

    @Inject
    Login xiaoming;

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

        DaggerUserComponent.builder()
                .userModule(new UserModule())
                .build()
                .inject(this); 

        xiaoming.getName(); //直接使用
    }

以上就是我們通常的使用方式,而SecondActivity中就體現了我們一開始說到的問題。接下來我用新方法實現一遍。

1.首先我們在AppComponent中統一注入AndroidSupportInjectionModule

@Singleton
@Component(modules = {
        AppModule.class,
        StorageModule.class,
        BuildersModule.class,
        AndroidSupportInjectionModule.class
})
interface AppComponent extends AndroidInjector<MyApplication>{

    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<MyApplication> {}

}

AndroidInjectionModule源碼如下 ,AndroidSupportInjectionModule可以額外支持V4包下的Fragment.

@Beta
@Module
public abstract class AndroidInjectionModule {
  @Multibinds
  abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
      activityInjectorFactories();

  @Multibinds
  abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
      fragmentInjectorFactories();

  @Multibinds
  abstract Map<Class<? extends Service>, AndroidInjector.Factory<? extends Service>>
      serviceInjectorFactories();

  @Multibinds
  abstract Map<
          Class<? extends BroadcastReceiver>, AndroidInjector.Factory<? extends BroadcastReceiver>>
      broadcastReceiverInjectorFactories();

  @Multibinds
  abstract Map<
          Class<? extends ContentProvider>, AndroidInjector.Factory<? extends ContentProvider>>
      contentProviderInjectorFactories();

  private AndroidInjectionModule() {}
}

可以看到它可以幫我們將安卓中四大組件以及Fragment進行綁定。

BuildersModule是我爲了統一管理依賴於AppComponentModule添加的中間件。如下

@Module(subcomponents = {UserComponent.class})
public abstract class BuildersModule {

    @Binds
    @IntoMap
    @ActivityKey(SecondActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindSecondActivityInjectorFactory(UserComponent.Builder builder);

}

有了BuildersModule,我們之前的UserModule保持不變,UserComponent修改如下

@Subcomponent(modules = {UserModule.class})
public interface UserComponent extends AndroidInjector<SecondActivity> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<SecondActivity> {}

}

UserComponent需繼承自AndroidInjector,在該子組件中含有一個抽象類Builder,繼承自AndroidInjector.Builder,並由@Subcomponent.Builder註解。注意:這裏UserComponent使用@Subcomponent註解是爲了使用AppComponent中的AndroidSupportInjectionModule.

接下來自定義MyApplication繼承DaggerApplication

public class MyApplication extends DaggerApplication {

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerAppComponent.builder().create(this);
    }
}

如果我們代碼中已經有繼承的父類Application,則可以根據DaggerApplication源碼去實現對應接口。

最後一步Activity繼承DaggerAppCompatActivity,在super.onCreate(savedInstanceState)之前調用AndroidInjection.inject(this).

public class SecondActivity extends DaggerAppCompatActivity {

    @Inject
    Login xiaoming;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        xiaoming.getName(); //直接使用
    }

當然了,我們可以將AndroidInjection.inject(this)封裝進BaseActivity,這樣使用起來更酸爽。

有些人會覺得這寫起來比之前麻煩多了,爲了解決這個問題,在最近的2.11版中新增了@ContributesAndroidInjector註解。有了它我們上面的UserComponent可以不要了。(驚不驚喜,意不意外!)

@Module
public abstract class BuildersModule {

    @ContributesAndroidInjector(modules = UserModule.class)
    abstract SecondActivity secondActivityInjector();
}

我們可以看一下自動生成的代碼:

@Module(subcomponents = BuildersModule_SecondActivityInjector.SecondActivitySubcomponent.class)
public abstract class BuildersModule_SecondActivityInjector {
  private BuildersModule_SecondActivityInjector() {}

  @Binds
  @IntoMap
  @ActivityKey(SecondActivity.class)
  abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
      SecondActivitySubcomponent.Builder builder);

  @Subcomponent(modules = UserModule.class)
  public interface SecondActivitySubcomponent extends AndroidInjector<SecondActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<SecondActivity> {}
  }
}

是不是一毛一樣。。。不要怪我不早說,我也是爲了大家可以更多的瞭解其中的細節。

3.工作原理

那麼它是如何工作的?(前方一大堆代碼亂入,不重要部分已去除)

我們首先從MyApplication開始,在我們的父類DaggerApplication幫我們實現了注入。

public abstract class DaggerApplication extends Application{

  @Inject DispatchingAndroidInjector<Activity> activityInjector;

  @Override
  public void onCreate() {
    super.onCreate();
    injectIfNecessary();
  }

  @ForOverride
  protected abstract AndroidInjector<? extends DaggerApplication> applicationInjector();

  private void injectIfNecessary() {
    AndroidInjector<DaggerApplication> applicationInjector =
              (AndroidInjector<DaggerApplication>) applicationInjector();
          applicationInjector.inject(this); //這裏注入
  }

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

注入時的具體內容

private Provider<AndroidInjector.Factory<? extends Activity>> bindAndroidInjectorFactoryProvider;

private void initialize(final Builder builder) {

    //這裏不就是我們使用@ContributesAndroidInjector生成的Subcomponent嗎,在這裏進行了實現
    this.secondActivitySubcomponentBuilderProvider =
        new dagger.internal.Factory<
            BuildersModule_SecondActivityInjector.SecondActivitySubcomponent.Builder>() {
          @Override
          public BuildersModule_SecondActivityInjector.SecondActivitySubcomponent.Builder get() {
            return new SecondActivitySubcomponentBuilder();
          }
        };

    this.bindAndroidInjectorFactoryProvider = (Provider) secondActivitySubcomponentBuilderProvider;
    //再將所有的Activity對應的SubcomponentBuilder存進MapProviderFactory
    this.mapOfClassOfAndProviderOfFactoryOfProvider =
        MapProviderFactory
            .<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>builder(1)
            .put(SecondActivity.class, bindAndroidInjectorFactoryProvider)
            .build();

    this.dispatchingAndroidInjectorProvider =
        DispatchingAndroidInjector_Factory.create(mapOfClassOfAndProviderOfFactoryOfProvider);

    //將四大組件及Fragment全部放入
    this.myApplicationMembersInjector =
        MyApplication_MembersInjector.create(dispatchingAndroidInjectorProvider);
 }

  @Override
  public void inject(MyApplication arg0) {
    myApplicationMembersInjector.injectMembers(arg0); //注入
  }
private final class SecondActivitySubcomponentBuilder
      extends BuildersModule_ContributeSecondActivityInjector.SecondActivitySubcomponent.Builder {
    private UserModule userModule;

    private SecondActivity seedInstance;

    @Override
    public BuildersModule_ContributeSecondActivityInjector.SecondActivitySubcomponent build() {
      if (userModule == null) {
        this.userModule = new UserModule();
      }
      if (seedInstance == null) {
        throw new IllegalStateException(SecondActivity.class.getCanonicalName() + " must be set");
      }
      return new SecondActivitySubcomponentImpl(this);
    }

    @Override
    public void seedInstance(SecondActivity arg0) {
      this.seedInstance = Preconditions.checkNotNull(arg0);
    }
  }

當我們在Activity調用AndroidInjection.inject(this)時,從Application獲取一個DispatchingAndroidInjector<Activity>,並將您的activity傳遞給inject(activity)

  public static void inject(Activity activity) {
    Application application = activity.getApplication();
    //獲取DaggerApplication中的activityInjector
    AndroidInjector<Activity> activityInjector =
        ((HasActivityInjector) application).activityInjector();

    activityInjector.inject(activity);
  }

DispatchingAndroidInjector通過AndroidInjector.Factory創建AndroidInjector,並將您的activity傳遞至SecondActivitySubcomponentImpl中。

@Beta
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {

  @Inject
  DispatchingAndroidInjector(
      Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) {
    this.injectorFactories = injectorFactories;
  }

  @CanIgnoreReturnValue
  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().getCanonicalName());

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

    }
  }

  @Override
  public void inject(T instance) {
    boolean wasInjected = maybeInject(instance);
    if (!wasInjected) {
      throw new IllegalArgumentException(errorMessageSuggestions(instance));
    }
  }
}
private final class SecondActivitySubcomponentImpl
      implements BuildersModule_ContributeSecondActivityInjector.SecondActivitySubcomponent {
    private Provider<Login> provideXiaoMingUserProvider;

    private MembersInjector<SecondActivity> secondActivityMembersInjector;

    private SecondActivitySubcomponentImpl(SecondActivitySubcomponentBuilder builder) {
      assert builder != null;
      initialize(builder);
    }

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

      this.provideXiaoMingUserProvider =
          UserModule_ProvideXiaoMingUserFactory.create(builder.userModule);

      this.secondActivityMembersInjector =
          SecondActivity_MembersInjector.create(
              DaggerAppComponent.this.dispatchingAndroidInjectorProvider6,
              DaggerAppComponent.this.dispatchingAndroidInjectorProvider3,
              provideXiaoMingUserProvider);
    }

    @Override
    public void inject(SecondActivity arg0) {
      secondActivityMembersInjector.injectMembers(arg0);//注入
    }
  }

到此爲止,它的工作流程就是這樣。

細心地你其實這裏會發現,我們依賴的Module不需要我們像之前一樣去一個一個創建好去設置了,會默認實現它的無參構造方法。當然在這個Module中可以使用我們注入的Activity。如果你之前的Module有構造方法,試着去修改它、

4.其他

以上是以Activity作爲例子實現的,對於其他的四大組件以及Fragment,使用起來大同小異。比如使用Fragment,我們就將@ActivityKey替換爲@FragmentKeyAndroidInjection.inject(this)方法,在FragmentonAttach()中的super.onAttach()方法之前調用。當然如果你使用了@ContributesAndroidInjector則可以不用去管@xxxKey

@Module(subcomponents = {TestFragmentComponent.class})
public abstract class FragmentBuildersModule {

    @Binds
    @IntoMap
    @FragmentKey(TestFragment.class) //<-- 這裏
    abstract AndroidInjector.Factory<? extends Fragment>
    bindTopFragmentInjectorFactory(TestFragmentComponent.Builder builder);
}

@Subcomponent(modules = TestFragmentModule.class)
public interface TestFragmentComponent extends AndroidInjector<TestFragment> {

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

    }
}

@Module
public class TestFragmentModule {

}

public class TestFragment extends Fragment {

    @Override
    public void onAttach(Activity activity) {
        AndroidInjection.inject(this); //<--這裏
        super.onAttach(activity);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment, container, false);
    }
}

我已經將這些代碼上傳到了我的Github,大家可以在分支dagger2.11下查看新的寫法。(主分支爲舊寫法,方便大家對比。)

參考

  1. Dagger & Android官方文檔

  2. Android and Dagger 2.10 AndroidInjector

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