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


  • 2018-08-07 20:00:42

    xUtils3.0版本的发送同步网络请求的方式

    对于Android开发来说,基本都是用异步来从网络上请求数据,很少用到同步请求的。近日项目有个地方需要使用到同步请求(以我目前的知识储备来说好像只能用同步请求来解决这个问题了),去网上搜索相关资料,又没有找到什么明确的使用方法。所以记下来,以备不时之需。

  • 2018-08-14 23:35:28

    Retrofit 设置 超时时间

    今天开发的时候遇到一个网络请求超时的问题,后台处理是成功的,但是移动端返回的总是提示请求超时,在设置了retrofit请求超时的时间延长以后,就可以请求成功了,下面是配置的方法:

  • 2018-08-16 16:10:43

    Laravel 跨域解决方案

    我们在用 laravel 进行开发的时候,特别是前后端完全分离的时候,由于前端项目运行在自己机器的指定端口(也可能是其他人的机器) , 例如 localhost:8000 , 而 laravel 程序又运行在另一个端口,这样就跨域了,而由于浏览器的同源策略,跨域请求是非法的。其实这个问题很好解决,只需要添加一个中间件就可以了。

  • 2018-08-18 20:30:12

    laravel5.5 路由分割成不同文件

    routes.php/api.php文件用来放置laravel路由,当项目越来越大,相应的路由文件也会越来越多。如果能够将不同功能的路由分割到不同的文件,那么对以后的维护将很有帮助。

  • 2018-08-20 15:26:19

    关于OnTouch 和OnClick同时调用冲突的解决方案

    大家在搞轮播图的时候会碰到这样的情况,点击进入webview界面,长按轮播图停止轮播,手松开图又开始轮播,这里就涉及到了OnTouch 和OnClick同时调用。两者是有冲突的。这里简单介绍,给大家提供思路。

  • 2018-08-20 15:29:11

    揭开RecyclerView的神秘面纱(二):处理RecyclerView的点击事件

    主要讲述了RecyclerView的基本使用方法,不同的布局管理器而造成的多样化展示方式,展示了数据之后,一般都会与用户进行交互,因此我们需要处理用户的点击事件。在ListView和GridView提供了onItemClickListener这个监听器,然而我们查找RecyclerView的API却没有类似的监听器,因此我们需要自己手动处理它的点击事件。 以下提供两种方法来实现处理RecyclerView点击事件的功能,以下代码均基于上一篇文章的代码做出修改。

  • 2018-08-20 22:58:46

    onInterceptTouchEvent和onTouchEvent调用关系详解 ...

    老实说,这两个小东东实在是太麻烦了,很不好懂,我自己那api文档都头晕,在网上找到很多资料,才知道是怎么回事,这里总结一下,记住这个原则就会很清楚了: