Dagger 2 在 Android 上的用法

2020-11-22 23:14:52

参考地址 Dagger 2 在 Android 上的使用(六)

本文对系列的第一篇文章中的例子进行了分析总结。

本文首发:http://yuweiguocn.github.io/

《天净沙·秋思》
枯藤老树昏鸦,小桥流水人家,古道西风瘦马。
夕阳西下,断肠人在天涯。
-元代,马致远

Dagger 2 在 Android 上的使用(一)
Dagger 2 在 Android 上的使用(二)
Dagger 2 在 Android 上的使用(三)
Dagger 2 在 Android 上的使用(四)
Dagger 2 在 Android 上的使用(五)

前言

在前面的文章我们介绍了Dagger2 中的大部分注解的使用,接下来我们从源码角度分析下第一篇文章中例子的原理。

AppComponent

自定义组件AppComponent继承了AndroidInjector接口类,指定了Module类ActivityBindingModule和Dagger2中的AndroidSupportInjectionModule:

@Component(modules = {ActivityBindingModule.class,AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector{
}

AndroidInjector接口中包含一个inject用于注入的方法,和一个抽象Builder类,其中用到了我们在上文中介绍的@BindsInstance注解,用于将注入的实例绑定到构建的依赖图中。

public interface AndroidInjector{

  void inject(T instance);

  interface Factory{

    AndroidInjectorcreate(T instance);
  }

  abstract class Builderimplements AndroidInjector.Factory{
    @Override
    public final AndroidInjectorcreate(T instance) {
      seedInstance(instance);
      return build();
    }

    @BindsInstance
    public abstract void seedInstance(T instance);

    public abstract AndroidInjectorbuild();
  }
}

AndroidSupportInjectionModule中用@Multibinds注解声明了Support中Fragment的多绑定,包括Fragment的Class的key和String的key两个集合,还用到了@Moduleincludes属性用于引入其它的Module进行组合:

@Beta
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
  @Multibinds
  abstract Map<class, AndroidInjector.Factory>
      supportFragmentInjectorFactories();

  @Multibinds
  abstract Map<string, androidinjector.factory>
      supportFragmentInjectorFactoriesWithStringKeys();

  private AndroidSupportInjectionModule() {}
}

AndroidInjectionModule类中和AndroidSupportInjectionModule类似,声明了Android中四大组件Activity、Service、BroadcastReceiver、ContentProvider和Fragment的多绑定,只所以需要声明是由于这些Map集合有可能为空

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

  @Multibinds
  abstract Map<string, androidinjector.factory>
      activityInjectorFactoriesWithStringKeys();

  @Multibinds
  abstract Map<class, AndroidInjector.Factory>
      fragmentInjectorFactories();

  @Multibinds
  abstract Map<string, androidinjector.factory>
      fragmentInjectorFactoriesWithStringKeys();

  @Multibinds
  abstract Map<class, AndroidInjector.Factory>
      serviceInjectorFactories();

  @Multibinds
  abstract Map<string, androidinjector.factory>
      serviceInjectorFactoriesWithStringKeys();

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

  @Multibinds
  abstract Map<string, androidinjector.factory>
      broadcastReceiverInjectorFactoriesWithStringKeys();

  @Multibinds
  abstract Map<class, AndroidInjector.Factory>
      contentProviderInjectorFactories();

  @Multibinds
  abstract Map<string, androidinjector.factory>
      contentProviderInjectorFactoriesWithStringKeys();

  private AndroidInjectionModule() {}
}

ActivityBindingModule

下面是我们自定义的用于Activity绑定的Module类:

@Module
public abstract class ActivityBindingModule {

    @ActivityScope
    @ContributesAndroidInjector(modules = HomeModule.class)
    abstract HomeActivity contributeHomeActivity();

}

注解@ContributesAndroidInjector用于为该方法返回类型生成一个AndroidInjector。用在Module中的无参抽象方法上,返回参数为具体的Android框架类型(如:HomeActivity、MyFragment、MyService等),指定的Module将会被安装到生成的Subcomponent上。上面的代码将会生成下面的类:

@Module(subcomponents = ActivityBindingModule_ContributeHomeActivity.HomeActivitySubcomponent.class)
public abstract class ActivityBindingModule_ContributeHomeActivity {
  private ActivityBindingModule_ContributeHomeActivity() {}

  @Binds
  @IntoMap
  @ActivityKey(HomeActivity.class)
  abstract AndroidInjector.Factory bindAndroidInjectorFactory(
      HomeActivitySubcomponent.Builder builder);

  @Subcomponent(modules = HomeModule.class)
  @ActivityScope
  public interface HomeActivitySubcomponent extends AndroidInjector{
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder{}
  }
}

这里使用了多绑定以Activity的class为key和生成的子组件的Builder类为value绑定到了Map集合中。

DaggerAppComponent

上面类中的多绑定会在生成的Component类中生成提供该集合的方法,以及生成的子组件接口的实现类:

public final class DaggerAppComponent implements AppComponent {

  private ProviderhomeActivitySubcomponentBuilderProvider;

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

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

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

  private Map<class, Provider<androidinjector.factory>>
      getMapOfClassOfAndProviderOfFactoryOf() {
    return MapBuilder
        .<class, Provider<androidinjector.factory>>
            newMapBuilder(1)
        .put(HomeActivity.class, (Provider) homeActivitySubcomponentBuilderProvider)
        .build();
  }

  private DispatchingAndroidInjectorgetDispatchingAndroidInjectorOfActivity() {
    return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(
        getMapOfClassOfAndProviderOfFactoryOf(),
        Collections.<string, provider<androidinjector.factory>>emptyMap());
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.homeActivitySubcomponentBuilderProvider =
        new Provider<
            ActivityBindingModule_ContributeHomeActivity.HomeActivitySubcomponent.Builder>() {
          @Override
          public ActivityBindingModule_ContributeHomeActivity.HomeActivitySubcomponent.Builder
              get() {
            return new HomeActivitySubcomponentBuilder();
          }
        };
  }

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

  private App injectApp(App instance) {
    DaggerApplication_MembersInjector.injectActivityInjector(
        instance, getDispatchingAndroidInjectorOfActivity());
    return instance;
  }

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

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

  private final class HomeActivitySubcomponentBuilder
      extends ActivityBindingModule_ContributeHomeActivity.HomeActivitySubcomponent.Builder {
    private HomeActivity seedInstance;

    @Override
    public ActivityBindingModule_ContributeHomeActivity.HomeActivitySubcomponent build() {
      if (seedInstance == null) {
        throw new IllegalStateException(HomeActivity.class.getCanonicalName() + " must be set");
      }
      return new HomeActivitySubcomponentImpl(this);
    }

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

  private final class HomeActivitySubcomponentImpl
      implements ActivityBindingModule_ContributeHomeActivity.HomeActivitySubcomponent {
    private ProviderbindPresenterProvider;

    private HomeActivitySubcomponentImpl(HomeActivitySubcomponentBuilder builder) {
      initialize(builder);
    }

    @SuppressWarnings("unchecked")
    private void initialize(final HomeActivitySubcomponentBuilder builder) {
      this.bindPresenterProvider = DoubleCheck.provider((Provider) HomePresenter_Factory.create());
    }

    @Override
    public void inject(HomeActivity arg0) {
      injectHomeActivity(arg0);
    }

    private HomeActivity injectHomeActivity(HomeActivity instance) {
      DaggerActivity_MembersInjector.injectFragmentInjector(
          instance, DaggerAppComponent.this.getDispatchingAndroidInjectorOfFragment());
      HomeActivity_MembersInjector.injectMPresenter(instance, bindPresenterProvider.get());
      return instance;
    }
  }
}

DaggerApplication

我们在Application中继承了DaggerApplication类,并实现了applicationInjector方法返回了AppComponent类的实例:

public class App extends DaggerApplication {

    @Override
    protected AndroidInjector applicationInjector() {
        return DaggerAppComponent.create();
    }

}

DaggerApplication在onCreate方法中调用了我们实现的applicationInjector方法,然后调用inject方法完成了对成员变量的注入。

@Beta
public abstract class DaggerApplication extends Application
    implements HasActivityInjector,
        HasFragmentInjector,
        HasServiceInjector,
        HasBroadcastReceiverInjector,
        HasContentProviderInjector {

  @Inject DispatchingAndroidInjectoractivityInjector;
  @Inject DispatchingAndroidInjectorbroadcastReceiverInjector;
  @Inject DispatchingAndroidInjectorfragmentInjector;
  @Inject DispatchingAndroidInjectorserviceInjector;
  @Inject DispatchingAndroidInjectorcontentProviderInjector;
  private volatile boolean needToInject = true;

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


  @ForOverride
  protected abstract AndroidInjector applicationInjector();

  private void injectIfNecessary() {
    if (needToInject) {
      synchronized (this) {
        if (needToInject) {
          @SuppressWarnings("unchecked")
          AndroidInjectorapplicationInjector =
              (AndroidInjector) applicationInjector();
          applicationInjector.inject(this);
          if (needToInject) {
            throw new IllegalStateException(
                "The AndroidInjector returned from applicationInjector() did not inject the "
                    + "DaggerApplication");
          }
        }
      }
    }
  }

  @Inject
  void setInjected() {
    needToInject = false;
  }

  @Override
  public DispatchingAndroidInjectoractivityInjector() {
    return activityInjector;
  }

  @Override
  public DispatchingAndroidInjectorfragmentInjector() {
    return fragmentInjector;
  }

  @Override
  public DispatchingAndroidInjectorbroadcastReceiverInjector() {
    return broadcastReceiverInjector;
  }

  @Override
  public DispatchingAndroidInjectorserviceInjector() {
    return serviceInjector;
  }

  @Override
  public AndroidInjectorcontentProviderInjector() {
    injectIfNecessary();
    return contentProviderInjector;
  }
}

DaggerActivity

我们的Activity继承自了DaggerActivity,DaggerActivity类中实现HasFragmentInjector接口用于Fragment的注入,在onCreate方法中使用AndroidInjection类完成了Activity中所需的依赖注入。

@Beta
public abstract class DaggerActivity extends Activity implements HasFragmentInjector {

  @Inject DispatchingAndroidInjectorfragmentInjector;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
  }

  @Override
  public AndroidInjectorfragmentInjector() {
    return fragmentInjector;
  }
}

AndroidInjection

AndroidInjection是一个工具类,用于对Android框架中Activity、Fragment、Service、BroadcastReceiver、ContentProvider进行依赖注入。AndroidInjection会从Application中获取activityInjector方法的值进行依赖注入。

public final class AndroidInjection {
  private static final String TAG = "dagger.android";

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

    AndroidInjectoractivityInjector =
        ((HasActivityInjector) application).activityInjector();
    checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());

    activityInjector.inject(activity);
  }

  ...
  private AndroidInjection() {}
}

DispatchingAndroidInjector

DaggerApplication中activityInjector方法返回的是DispatchingAndroidInjector类型,DispatchingAndroidInjector用于Android框架类型实例的成员变量的注入,首先对构造方法传入的两个集合进行了合并,然后在注入时根据类名从集合中获取对应实现类完成成员变量的注入。

public final class DispatchingAndroidInjectorimplements AndroidInjector{
  private static final String NO_SUPERTYPES_BOUND_FORMAT =
      "No injector factory bound for Class<%s>";
  private static final String SUPERTYPES_BOUND_FORMAT =
      "No injector factory bound for Class<%1$s>. Injector factories were bound for supertypes "
          + "of %1$s: %2$s. Did you mean to bind an injector factory for the subtype?";

  private final Map<string, provider<androidinjector.factory>> injectorFactories;

  @Inject
  DispatchingAndroidInjector(
      Map<class, Provider<factory>> injectorFactoriesWithClassKeys,
      Map<string, provider<factory>> injectorFactoriesWithStringKeys) {
    this.injectorFactories = merge(injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
  }

  private staticMapmerge(
      Map<class, V> classKeyedMap, MapstringKeyedMap) {
    if (classKeyedMap.isEmpty()) {
      return stringKeyedMap;
    }

    Mapmerged =
        newLinkedHashMapWithExpectedSize(classKeyedMap.size() + stringKeyedMap.size());
    merged.putAll(stringKeyedMap);
    for (Entry<class, V> entry : classKeyedMap.entrySet()) {
      merged.put(entry.getKey().getName(), entry.getValue());
    }

    return Collections.unmodifiableMap(merged);
  }

  @CanIgnoreReturnValue
  public boolean maybeInject(T instance) {
    Provider<androidinjector.factory> factoryProvider =
        injectorFactories.get(instance.getClass().getName());
    if (factoryProvider == null) {
      return false;
    }

    @SuppressWarnings("unchecked")
    AndroidInjector.Factoryfactory = (AndroidInjector.Factory) factoryProvider.get();
    try {
      AndroidInjectorinjector =
          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);
    }
  }

  @Override
  public void inject(T instance) {
    boolean wasInjected = maybeInject(instance);
    if (!wasInjected) {
      throw new IllegalArgumentException(errorMessageSuggestions(instance));
    }
  }

  @Beta
  public static final class InvalidInjectorBindingException extends RuntimeException {
    InvalidInjectorBindingException(String message, ClassCastException cause) {
      super(message, cause);
    }
  }

  private String errorMessageSuggestions(T instance) {
    Listsuggestions = new ArrayList<>();
    for (Class clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
      if (injectorFactories.containsKey(clazz.getCanonicalName())) {
        suggestions.add(clazz.getCanonicalName());
      }
    }

    return suggestions.isEmpty()
        ? String.format(NO_SUPERTYPES_BOUND_FORMAT, instance.getClass().getCanonicalName())
        : String.format(
            SUPERTYPES_BOUND_FORMAT, instance.getClass().getCanonicalName(), suggestions);
  }
}

总结

Dagger2使用多绑定和子组件功能将需要成员变量注入类的class和生成子组件实现类存入到Application的Map集合中,在Activity#onCreate方法中通过类名从Map集合中获取对应实现类完成了成员变量注入。

参考

  • https://google.github.io/dagger/

  • https://www.jianshu.com/p/24af4c102f62

  • http://www.cnblogs.com/tiantianbyconan/p/5092083.html


  • 2019-05-29 14:19:19

    解决Git中fatal: refusing to merge unrelated histories

    Git的报错 在使用Git的过程中有时会出现一些问题,那么在解决了每个问题的时候,都需要去总结记录下来,下次不再犯。 一、fatal: refusing to merge unrelated histories 今天在使用Git创建项目的时候,在两个分支合并的时候,出现了下面的这个错误。

  • 2019-05-29 15:47:51

    撤销commit

    在git push的时候,有时候我们会想办法撤销git commit的内容

  • 2019-06-03 00:07:32

    Android程序Crash时的异常上报

    家都知道,android应用不可避免的会发生crash,无论你的程序写的多完美,总是无法完全避免crash的发生,可能是由于android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络状况。当crash发生时,系统会kill掉你的程序,表现就是闪退或者程序已停止运行,这对用户来说是很不友好的,也是开发者所不愿意看到的,更糟糕的是,当用户发生了crash,开发者却无法得知程序为何crash,即便你想去解决这个crash,但是由于你无法知道用户当时的crash信息,所以你也无能为力。是否真的这样呢,其实android中有处理这类问题的方法,请看下面Thread类中的一个方法#setDefaultUncaughtExceptionHandler

  • 2019-06-04 16:40:30

    为了美观当网页图片不存在时不显示叉叉图片

    当在页面显示的时候,万一图片被移动了位置或者丢失的话,将会在页面显示一个带X的图片,很是影响用户的体验。即使使用alt属性给出了”图片XX”的提示信息,也起不了多大作用。