Dagger2与AndroidInjector详解

2020-11-22 23:31:22

参考地址 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();
  }
}123456789101112131415

但是有一些问题:

  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'}12345

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;
    }
}123456789101112131415161718192021

假设初始化name小明password******。我们使用Dagger进行依赖注入,实现UserModule

@Modulepublic class UserModule {

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

}12345678910111213

相对应的UserComponent如下

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

那么对应的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(); //直接使用
    }1234567891011121314151617

以上就是我们通常的使用方式,而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> {}

}12345678910111213

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

@Beta
@Modulepublic 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() {}
}12345678910111213141516171819202122232425262728

可以看到它可以帮我们将安卓中四大组件以及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);

}12345678910

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

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

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

}1234567

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);
    }
}1234567

如果我们代码中已经有继承的父类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(); //直接使用
    }123456789101112

当然了,我们可以将AndroidInjection.inject(this)封装进BaseActivity,这样使用起来更酸爽。

有些人会觉得这写起来比之前麻烦多了,为了解决这个问题,在最近的2.11版中新增了@ContributesAndroidInjector注解。有了它我们上面的UserComponent可以不要了。(惊不惊喜,意不意外!)

@Modulepublic abstract class BuildersModule {

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

我们可以看一下自动生成的代码:

@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> {}
  }
}12345678910111213141516

是不是一毛一样。。。不要怪我不早说,我也是为了大家可以更多的了解其中的细节。

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;
  }
}123456789101112131415161718192021222324

注入时的具体内容

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); //注入
  }12345678910111213141516171819202122232425262728293031323334
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);
    }
  }12345678910111213141516171819202122

当我们在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);
  }123456789

DispatchingAndroidInjector通过AndroidInjector.Factory创建AndroidInjector,并将您的activity传递至SecondActivitySubcomponentImpl中。

@Betapublic 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));
    }
  }
}123456789101112131415161718192021222324252627282930313233343536373839404142
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);//注入
    }
  }1234567891011121314151617181920212223242526272829

到此为止,它的工作流程就是这样。

细心地你其实这里会发现,我们依赖的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> {

    }
}

@Modulepublic 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);
    }
}1234567891011121314151617181920212223242526272829303132333435363738

我已经将这些代码上传到了我的Github,大家可以在分支dagger2.11下查看新的写法。(主分支为旧写法,方便大家对比。)

参考

  1. Dagger & Android官方文档

  2. Android and Dagger 2.10 AndroidInjector


  • 2017-02-13 17:50:05

    cURL error 60: SSL certificate problem: unable to get local issuer certificate

    Drupal 8 version uses Guzzle Http Client internally, but under the hood it may use cURL or PHP internals. If you installed PHP cURL on your PHP server it typically uses cURL and you may see an exception with error Peer certificate cannot be authenticated with known CA certificates or error code CURLE_SSL_CACERT (60).

  • 2017-02-16 08:09:01

    HTML中PRE和p的区别

    pre 元素可定义预格式化的文本。被包围在 pre 元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。 <pre> 标签的一个常见应用就是用来表示计算机的源代码。

  • 2017-02-16 15:14:14

    动态加载js和css

    开发过程中经常需要动态加载js和css,今天特意总结了一下常用的方法。