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


  • 2019-12-06 10:47:29

    date-fns日期工具的使用方法详解

    isToday() 判断传入日期是否为今天 isYesterday() 判断传入日期是否为昨天 isTomorrow() 判断传入日期是否为 format() 日期格式化 addDays() 获得当前日期之后的日期 addHours() 获得当前时间n小时之后的时间点 addMinutes() 获得当前时间n分钟之后的时间 addMonths() 获得当前月之后n个月的月份 subDays() 获得当前时间之前n天的时间 subHours() 获得当前时间之前n小时的时间 subMinutes() 获得当前时间之前n分钟的时间 subMonths() 获得当前时间之前n个月的时间 differenceInYears() 获得两个时间相差的年份 differenceInWeeks() 获得两个时间相差的周数 differenceInDays() 获得两个时间相差的天数 differenceInHours() 获得两个时间相差的小时数 differenceInMinutes() 获得两个时间相差的分钟数

  • 2019-12-06 10:49:39

    npm 查看源 换源

    npm,cnpm,查看源,切换源,npm config set registry https://registry.npmjs.org

  • 2019-12-06 11:01:31

    npm发布包流程详解 有demo

    npm发布包步骤,以及踩过的坑(见红颜色标准): 1.注册npm账号,并完成Email认证(否则最后一步提交会报Email错误) 2.npm添加用户或登陆:npm adduser 或 npm login

  • 2019-12-06 13:16:18

    vue mixins组件复用的几种方式

    最近在做项目的时候,研究了mixins,此功能有妙处。用的时候有这样一个场景,页面的风格不同,但是执行的方法,和需要的数据非常的相似。我们是否要写两种组件呢?还是保留一个并且然后另个一并兼容另一个呢? 不管以上那种方式都不是很合理,因为组件写成2个,不仅麻烦而且维护麻烦;第二种虽然做了兼容但是页面逻辑造成混乱,必然不清晰;有没有好的方法,有那就是用vue的混合插件mixins。混合在Vue是为了提出相似的数据和功能,使代码易懂,简单、清晰。

  • 2019-12-06 13:26:30

    vue的mixins混入合并规则

    混入minxins:分发vue组件中可复用功能的灵活方式。混入对象可以包含任意组件选项。组件使用混入对象时,所有混入对象的选项将混入该组件本身的选项。

  • 2019-12-06 16:50:34

    Intellij idea 如何关闭无用的提示

    Linux:Settings —> Editor —> Inspections —> General —> Duplicated Code Mac:Preferences --> Editor —> Inspections —> General —> Duplicated Code fragment 将对应的勾去掉。

  • 2019-12-09 15:36:56

    神秘的 shadow-dom 浅析,shadow-root

    顾名思义, shadow-dom,直译的话就是 影子dom ?我觉得可以理解为潜藏在黑暗中的 DOM 结构,也就是我们无法直接控制操纵的 DOM 结构。前端同学经常用开发者工具的话,查看 DOM 结构的时候,肯定看到过下面这样的结构:

  • 2019-12-10 11:13:50

    前端实战-基于Nuxt的SVG使用

    虽然我们在日常开发的时候,在使用iview 或者element ui等组件时,通常会包含一些常用icon;但是在面对一些特定的需求时,或者自己想high一下,这些通用的icon并不能很好的满足我们。这个时候我们可能会拿到一些SVG适量图,但是怎么去使用这些矢量图呢。

  • 2019-12-10 11:15:08

    用CSS给SVG 的内容添加样式

    SVG图形的一个最常见用例是图标系统,其中最常用的SVG sprite技术就是使用SVG<use> 元素在文档中任意位置“实例化”图标。 使用<use>元素实例化图标或任何其它的SVG元素或图像,给元素添加样式时经常会碰到一些问题。这篇文章的目的是尽可能给你介绍一些方法来解决:使用<use>引入的内容添加样式受限的问题。 但是在开始之前,我们先快速浏览一下SVG的主要结构和分组元素,然后慢慢进入use的世界中,以及shadow DOM,然后重回CSS的怀抱。我们会逐步讲解为什么给<use>内容添加样式会比较麻烦,以及有什么好的解决方案。