Dagger2

本文深入探讨Dagger2依赖注入框架的高级特性,包括依赖注入的多种方式、特殊情况处理、依赖注入时机、Component依赖、Subcomponents使用技巧、Multibindings及Activity注入等。

一、简介

Dagger2是一个依赖注入库,依赖注入(Dependency Injection)是基于控制反转(Inversion of Control)的概念上的,它强调的是类应该从外部获取其依赖类实例,不应该自己实例化依赖类。当依赖类实例从外界获取之后,就可以做到对象复用、方便测试等功能。

Dagger2通过注解处理来自动生成依赖关系图,而用来描述类依赖的Java注解规范是定义在Java Specification Request 330(JSR330)当中。

根据JSR330的规范,依赖注入按注入顺序有三种模式:

  1. 构造函数注入:注入构造函数参数
  2. 成员变量注入:注入成员变量,必须非private,不一定按照声明先后顺序进行注入
  3. 方法注入:注入方法参数,不一定按照声明先后顺序进行注入

因为注入先后顺序的限制,要注意到不能在先注入的依赖中使用到后注入的依赖。

Dagger2的依赖注入过程可以总结为依赖需求方通过一个中介从依赖提供方获取依赖。以下对每个角色进行解释:

  1. 依赖提供方(provider):使用@Module注解的类用来提供依赖,其中的方法使用@Provides进行注解。或者使用@Inject注解提供依赖。

  2. 依赖需求方(consumer):使用@Inject注解的构造函数、成员变量或方法。

  3. 中介(connector):使用@Component注解的接口,由Dagger2负责生成实现类,进行依赖提供方和需求方的对接。

Dagger2也不是万能的,存在着一些限制:

  1. 无法自动注入成员变量,如果想要注入成员变量的话,需要在@Component接口中定义方法。
  2. 无法注入private成员变量。

二、依赖注入

假设现在需要实例化一辆带有Engine的Car,普通的实例化方式是:

Car car = new Car(new Engine());

但是这种方式,每个需要创建Car的地方都要去初始化Engine,比较麻烦,而且耦合性高,这时候使用Dagger2这个框架就可以帮我们去注入我们需要的依赖。

Dagger2中依赖有两种提供方式,第一种是在构造函数使用@Inject,第二种是在@Module中使用@Provides,下面分别对两种方式进行举例:

@Inject提供依赖

1.依赖提供方为Car,所以为其构造函数加上@Inject注解表明可以提供依赖。而Car本身又依赖于Engine,所以Engine类也需要@Inject注解:

public class Car {
    private Engine mEngine;

    @Inject
    public Car(Engine engine) {
        mEngine = engine;
    }

    @Override
    public String toString() {
        return "Car{" + "mEngine=" + mEngine + '}';
    }
}

public class Engine {
    private String mName;

    @Inject
    public Engine() {
        mName = "default engine";
    }

    @Override
    public String toString() {
        return mName;
    }
}

2.然后声明依赖需求方,定义带有@Component注解的接口,里面的所定义方法的返回值为所需的实例:

@Component
public interface CarComponent {
    Car getCar();
}

3.使用Dagger2自动生成的Dagger前缀实现类获取实例,Dagger2会自动帮帮我们完成注入的步骤:

final Car car = DaggerCarComponent.create().getCar();

@Provides提供依赖

1.声明Car和Engine,因为采用的是Provides提供,所以不用加上@Inject注解了:

public class Car {
    private Engine mEngine;

    public Car(Engine engine) {
        mEngine = engine;
    }

    @Override
    public String toString() {
        return "Car{" + "mEngine=" + mEngine + '}' + super.toString();
    }
}

public class Engine {
    private String mName;

    public Engine() {
        mName = " provided engine ";
    }

    @Override
    public String toString() {
        return "Engine{" + "mName='" + mName + '\'' + '}' + super.toString();
    }
}

2.使用@Provides提供依赖需要另外声明一个使用@Module进行注解的具体类,里面的方法用来提供依赖,因此使用@Provides进行注解,返回所需要的依赖实例:

@Module
public class CarModule {
    @Provides
    Car provideCar(Engine engine) {
        return new Car(engine);
    }

    @Provides
    Engine provideEngine() {
        return new Engine();
    }
}

3.在@Component处声明我们提供好的Module,这样它才能查找到所需要的依赖:

@Component(modules = {CarModule.class})
public interface CarComponent {
    Car getCar();
}

4.获取实例:

final Car car = DaggerCarComponent.create().getCar();

当Component没有配置Module,或者Module只有默认无参构造函数时,直接使用上述方式进行实例化即可。但是当需要给Module传入参数时,就需要在Builder中传入自己实例化的Module:

final Car car = DaggerCarComponent.builder()
        .carModule(new CarModule("good engine")).build()
        .getCar();

三、依赖特殊情况

@Qualifier

使用@Provides注入依赖的方式,是通过返回值类型来判断调用哪个方法的,当多个@Provides方法返回同一类型对象时,就需要添加@Named方法来区分不同的注入方法。

1.依赖请求方需要使用@Named注解对同类型参数进行区分:

public class Person {
    private String mName;
    private String mAddress;

    @Inject
    public Person(@Named("Name") String name, @Named("Address") String address) {
        mName = name;
        mAddress = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "mName='" + mName + '\'' +
                ", mAddress='" + mAddress + '\'' +
                '}';
    }
}

2.依赖提供方也需要对不同参数使用@Named注解:

@Module
public class PersonModule {
    @Named("Name")
    @Provides
    String provideName() {
        return "小明";
    }

    @Named("Address")
    @Provides
    String provideAddress() {
        return "中国";
    }
}

@Named也可以使用自定义注解进行替换:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Address {
}

@Singleton

@Singleton注解实现了同范围内的单实例模式,在同一Component实例中获取到的依赖都是相同实例。具体使用方法是使用@Singleton对依赖提供方和Component进行注解:

1.对依赖提供方添加@Singleton注解,对于@Inject方式可以在class处进行注解,而对于@Provides可以在@Provides方法声明处进行注解:

@Singleton
public class Engine {
    private String mName;

    @Inject
    public Engine() {
        mName = "default engine";
    }

    @Override
    public String toString() {
        return "Engine{" + "mName='" + mName + '\'' + '}' + super.toString();
    }
}

2.Component也加上@Singleton注解,如果没有加上会产生未指定范围的Component引用了指定范围绑定的错误:

@Singleton
@Component
public interface CarComponent {
    Car car();
    Engine engine();
}

3.具体实例化使用的时候,同个Component实例获取的依赖实例是单实例:

final CarComponent carComponent = DaggerCarComponent.create();
final Car car1 = carComponent.car();
final Car car2 = carComponent.car();

@Singleton本身被@Scope注解了,此处的原理是依赖提供方和Component都添加了相同的@Scope范围限定注解(也必须相同,否则报错),在同一个Component实例当中,依赖提供方总是提供相同的实例,达到了单例模式的效果。所以自定义一个单纯的@Scope注解就可以达到@Singleton一样的效果了,例如:

@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface OnlyOne {
}

四、依赖注入时机

Dagger2中依赖注入时机可分为三种:直接注入、Lazy注入、Provider注入,下面首先对通用的依赖提供和Component进行声明。

通用的Integer依赖提供,内部保存一个计数器,并在依赖方法调用时输出日志:

@Module
public class CounterModule {
    private static final String TAG = "CounterModule";
    int next = 100;

    @Provides
    Integer provideInteger() {
        Log.e(TAG, "provideInteger: computing...");
        return next++;
    }
}

@Component类:

@Component(modules = {CounterModule.class})
public interface CounterComponent {
    DirectCounter directCounter();
    ProviderCounter providerCounter();
    LazyCounter lazyCounter();
}

最后统一运行:

final CounterComponent counterComponent = DaggerCounterComponent.create();

counterComponent.directCounter().print();
counterComponent.lazyCounter().print();
counterComponent.providerCounter().print();

接下来是三种注入方式的声明及输出结果:

1. 直接注入

直接注入就是普通声明:

public class DirectCounter {
    private static final String TAG = "DirectCounter";

    @Inject
    Integer value;

    @Inject
    public DirectCounter() {}

    public void print() {
        Log.e(TAG, "DirectCounter's print() is called");
        Log.e(TAG, "DirectCounter: " + value);
        Log.e(TAG, "DirectCounter: " + value);
        Log.e(TAG, "DirectCounter: " + value);
    }
}

运行后输出结果为:

com.timshinlee.dagger2test E/CounterModule: provideInteger: computing...
com.timshinlee.dagger2test E/DirectCounter: DirectCounter's print() is called
com.timshinlee.dagger2test E/DirectCounter: DirectCounter: 100
com.timshinlee.dagger2test E/DirectCounter: DirectCounter: 100
com.timshinlee.dagger2test E/DirectCounter: DirectCounter: 100

可以看到依赖字段在print()方法调用就已经注入,同个字段只注入一次。

2. Lazy注入

Lazy注入的话需要使用Lazy包装变量,变量获取使用lazy.get()

public class LazyCounter {
    private static final String TAG = "LazyCounter";

    @Inject
    Lazy<Integer> value;

    @Inject
    public LazyCounter() {
    }

    public void print() {
        Log.e(TAG, "LazyCounter's print() is called");
        Log.e(TAG, "LazyCounter: " + value.get());
        Log.e(TAG, "LazyCounter: " + value.get());
        Log.e(TAG, "LazyCounter: " + value.get());
    }
}

输出结果为:

 com.timshinlee.dagger2test E/LazyCounter: LazyCounter's print() is called
 com.timshinlee.dagger2test E/CounterModule: provideInteger: computing...
 com.timshinlee.dagger2test E/LazyCounter: LazyCounter: 101
 com.timshinlee.dagger2test E/LazyCounter: LazyCounter: 101
 com.timshinlee.dagger2test E/LazyCounter: LazyCounter: 101

可以看到print()方法先调用,在调用到注入变量时才进行初始化,因此称为Lazy注入,同时注入后多次调用也只注入一次。注意Lazy并不等同于@Singleton,Lazy只限于被包装变量的单次实例化。

3. Provider注入

与Lazy注入类似,Provider注入需要使用Provider包装变量:

public class ProviderCounter {
    private static final String TAG = "DirectCounter";

    @Inject
    Provider<Integer> value;

    @Inject
    public ProviderCounter() {}

    public void print() {
        Log.e(TAG, "ProviderCounter's print() is called");
        Log.e(TAG, "ProviderCounter: " + value.get());
        Log.e(TAG, "ProviderCounter: " + value.get());
        Log.e(TAG, "ProviderCounter: " + value.get());
    }
}

输出结果:

 com.timshinlee.dagger2test E/DirectCounter: ProviderCounter's print() is called
 com.timshinlee.dagger2test E/CounterModule: provideInteger: computing...
 com.timshinlee.dagger2test E/DirectCounter: ProviderCounter: 102
 com.timshinlee.dagger2test E/CounterModule: provideInteger: computing...
 com.timshinlee.dagger2test E/DirectCounter: ProviderCounter: 103
 com.timshinlee.dagger2test E/CounterModule: provideInteger: computing...
 com.timshinlee.dagger2test E/DirectCounter: ProviderCounter: 104

与Lazy类似,也是在调用到注入变量的时候才去进行注入,并且每次调用都会请求一次注入,获取最新的值。

五、Component依赖

Component注解中可以添加dependencies属性,属性值为其他Component,这样如果本Component中就可以使用其他Component所提供的依赖了。

如果依赖了其他Component,那么本Component在build的时候,就需要传入其他Component来初始化。

六、Subcomponents

Subcomponent是用来继承并扩展父Component的Component,可以把应用的绑定关系分成多个子关系组合,以便封装,或者在同一个Component中定义多个Scope。

与Java类似,子Component中的对象可以依赖自身以及父Component中的对象,但是父Component无法依赖子Component的对象;子component之间也无法互相依赖。

1. 实现步骤

此处将Car作为Component,Engine作为Subcomponent讲解用法:

1.Engine实体类和EngineModule的声明还是一样:

public class Engine {
    private String mName;

    public Engine() {
        mName = " new subcomponent engine ";
    }

    @Override
    public String toString() {
        return "Engine{" + "mName='" + mName + '\'' + '}';
    }
}

@Module
public class EngineModule {
    @Provides
    Engine provideEngine() {
        return new Engine();
    }
}

2.EngineComponent作为一个Subcomponent,使用@Subcomponent进行注解,同时需要声明一个@Subcomponent.Builder的Builder类,Builder中需要提供获取Subcomponent实例的方法,另外在Component中会自动声明的建造方法在@Subcomponent中如果要用到则需要手动进行声明:

@Subcomponent(modules = {EngineModule.class})
public interface EngineComponent {
    Engine engine();

    @Subcomponent.Builder
    interface Builder {
        Builder engineModule(EngineModule engineModule);

        EngineComponent build();
    }
}

3.主Component的实体类还是一样声明:

public class Car {
    private Engine mEngine;

    public Car(Engine engine) {
        mEngine = engine;
    }

    @Override
    public String toString() {
        return "Car{" + "mEngine=" + mEngine + '}';
    }
}

4.但是@Module中需要指明包含的Subcomponent,在需要使用Subcomponent实例的地方,使用Subcomponent的Builder去获取其实例,Dagger2会自动帮我们获取这里的Builder实例,可以加上Lazy、Provider等包装类:

@Module(subcomponents = {EngineComponent.class})
public class CarModule {
    @Provides
    Car provideCar(EngineComponent.Builder engineComponentBuilder) {
        return new Car(engineComponentBuilder.build().engine());
    }
}

5.Component声明如旧:

@Component(modules = {CarModule.class})
public interface CarComponent {
    Car car();
}

2. Subcomponent实现不同Scope

在Subcomponent的使用过程中,注意不能与其主Component声明同样的@Scope注解,否则范围内的唯一对象不确定是要保存在主Component还是Subcomponent当中。

而不同的Subcomponent可以声明同样的@Scope注解,虽然是同样的@Scope注解,但是生效范围不一样,所以里面的唯一实例是不同的。

@RootScope @Component
interface RootComponent {
  BadChildComponent.Builder badChildComponent(); // ERROR!
  SiblingComponentOne.Builder siblingComponentOne();
  SiblingComponentTwo.Builder siblingComponentTwo();
}

@RootScope @Subcomponent
interface BadChildComponent {...}

@ChildScope @Subcomponent
interface SiblingComponentOne {...}

@ChildScope @Subcomponent
interface SiblingComponentTwo {...}

因为Subcomponent是从Component中创建的,所以其生命周期范围是小于Component的,例如上述@ChildScope的范围就小于@RootScope。

3. Subcomponent实现封装

因为Subcomponent是依赖于Component而存在的,访问Subcomponent也需要经过Component才行,因此可以将不希望被随便访问的功能模块封装到Subcomponent当中,这样就限制了只能从所属的Component中去访问了,例如下列的DatabaseComponent就是一个Subcomponent,要访问就只能通过Component中定义的方法进行访问,达到封装的目的:

@Singleton
@Component(modules = DatabaseModule.class)
interface ApplicationComponent {
  Database database();
}

@Module(subcomponents = DatabaseComponent.class)
class DatabaseModule {
  @Provides
  @Singleton 
  Database provideDatabase(
      @NumberOfCores int numberOfCores,
      DatabaseComponent.Builder databaseComponentBuilder) {
    return databaseComponentBuilder
        .databaseImplModule(new DatabaseImplModule(numberOfCores / 2))
        .build()
        .database();
  }
}

@Module
class DatabaseImplModule {
  DatabaseImplModule(int concurrencyLevel) {}
  @Provides DatabaseConnectionPool provideDatabaseConnectionPool() {}
  @Provides DatabaseSchema provideDatabaseSchema() {}
}

@Subcomponent(modules = DatabaseImplModule.class)
interface DatabaseComponent {
  @PrivateToDatabase Database database();
}

七、Multibindings

Multibinding是把多个Module提供的对象合并到一个集合当中方便使用,可以用在多个Module提供多个对象组合一起实现功能的场景下。根据不同的容器类型可以分为Set和Map两种Multibinding。

1. Set multibindings

Set multibindings是以Set为容器集合所有Module中标注收集的对象,具体使用方法如下:

1.定义要收集的对象,一次返回一个使用@IntoSet,一次返回多个使用@ElementsIntoSet:

@Module
class MyModuleA {
  @Provides @IntoSet
  static String provideOneString() {
    return "ABC";
  }
}

@Module
class MyModuleB {
  @Provides @ElementsIntoSet
  static Set<String> provideSomeStrings() {
    return new HashSet<String>(Arrays.asList("DEF", "GHI"));
  }
}

2.所有Module中的值就可以集合到一起提供依赖:

@Component(modules = {MyModuleA.class, MyModuleB.class})
interface MyComponent {
  Set<String> strings();
}

multibinding中的Set也可以使用Lazy和Provider进行包装,例如Lazy<Set<Foo>>或者Provider<Set<Foo>>,但是不能使用Set<Provider<Foo>>

3.因为Multibinding也是使用Set中的泛型类型去区分具体绑定哪些对象,所以对于相同泛型类型不同Set的话,也需要使用@Qualifier进行区分:

@Module
class MyModuleC {
  @Provides @IntoSet
  @MyQualifier
  static Foo provideOneFoo(DepA depA, DepB depB) {
    return new Foo(depA, depB);
  }
}

@Module
class MyModuleD {
  @Provides
  static FooSetUser provideFooSetUser(@MyQualifier Set<Foo> foos) { ... }
}

2. Map multibindings

使用Map绑定的前提是编译时Map的key必须是已知的。绑定的方式是使用@IntoMap对方法进行注解,方法返回值为Map的value,同时需要一个自定义注解作为Map的key,如果需要区分同样使用@Qualifier进行区分。最后获取的Map<K, V>可以直接使用,或者进行包装Map<K, Provider<V>>

dagger.multibinding包中提供了@ClassKey、@StringKey、@IntKey、@LongKey等一系列预定义注解方便提供key:

@Module
class MyModule {
  @Provides @IntoMap
  @StringKey("foo")
  static Long provideFooValue() {
    return 100L;
  }

  @Provides @IntoMap
  @ClassKey(Thing.class)
  static String provideThingValue() {
    return "value for Thing";
  }
}

@Component(modules = MyModule.class)
interface MyComponent {
  Map<String, Long> longsByString();
  Map<Class<?>, String> stringsByClass();
}

对于枚举或者泛型等其他类型的Key,可以使用@MapKey自定义注解,提供一个返回方法为具体包含的类型(不能为数组),进行使用:

enum MyEnum {
  ABC, DEF;
}

@MapKey
@interface MyEnumKey {
  MyEnum value();
}

@MapKey
@interface MyNumberClassKey {
  Class<? extends Number> value();
}

@Module
class MyModule {
  @Provides @IntoMap
  @MyEnumKey(MyEnum.ABC)
  static String provideABCValue() {
    return "value for ABC";
  }

  @Provides @IntoMap
  @MyNumberClassKey(BigDecimal.class)
  static String provideBigDecimalValue() {
    return "value for BigDecimal";
  }
}

@Component(modules = MyModule.class)
interface MyComponent {
  Map<MyEnum, String> myEnumStringMap();
  Map<Class<? extends Number>, String> stringsByNumberClass();
}

如果Key复杂到包含多个成员类型,则可以设置@MapKey的unwrapValue属性为false,然后进行多种类型的声明,这种情况下就可以声明数组了:

@MapKey(unwrapValue = false)
@interface MyKey {
  String name();
  Class<?> implementingClass();
  int[] thresholds();
}

@Module
class MyModule {
  @Provides @IntoMap
  @MyKey(name = "abc", implementingClass = Abc.class, thresholds = {1, 5, 10})
  static String provideAbc1510Value() {
    return "foo";
  }
}

@Component(modules = MyModule.class)
interface MyComponent {
  Map<MyKey, String> myKeyStringMap();
}

使用复杂Key的情况下,使用map.get(key)方法需要传入Key实例,此时可以使用@AutoAnnotation注解一个静态方法来提供Key实例:

class MyComponentTest {
  @Test void testMyComponent() {
    MyComponent myComponent = DaggerMyComponent.create();
    assertThat(myComponent.myKeyStringMap()
        .get(createMyKey("abc", Abc.class, new int[] {1, 5, 10}))
        .isEqualTo("foo");
  }

  @AutoAnnotation
  static MyKey createMyKey(String name, Class<?> implementingClass, int[] thresholds) {
    return new AutoAnnotation_MyComponentTest_createMyKey(name, implementingClass, thresholds);
  }
}

Map multibindings要求编译时Key必须已知,对于编译时Key未知的情况,可以使用一种取巧方式把Set转化为Map,例如:

@Module
class MyModule {
  // 把整个Map.Entry作为Set的一个元素
  @Provides @IntoSet
  static Map.Entry<Foo, Bar> entryOne(...) {
    Foo key = ...;
    Bar value = ...;
    return new SimpleImmutableEntry(key, value);
  }
  // 把整个Map.Entry作为Set的一个元素
  @Provides @IntoSet
  static Map.Entry<Foo, Bar> entryTwo(...) {
    Foo key = ...;
    Bar value = ...;
    return new SimpleImmutableEntry(key, value);
  }
}

@Module
class MyMapModule {
  @Provides
  static Map<Foo, Bar> fooBarMap(Set<Map.Entry<Foo, Bar>> entries) {
    // 遍历这个Set里面的Map.Entry,另外创建一个Map将Map.Entry拆解保存起来获得Map
    Map<Foo, Bar> fooBarMap = new LinkedHashMap<>(entries.size());
    for (Map.Entry<Foo, Bar> entry : entries) {
      fooBarMap.put(entry.getKey(), entry.getValue());
    }
    return fooBarMap;
  }
}

注意此时如果使用Provider,则也需要包装到Entry的Value当中,否则Provider无法生效:

@Module
class MyModule {
  @Provides @IntoSet
  static Map.Entry<Foo, Provider<Bar>> entry(
      Provider<BarSubclass> barSubclassProvider) {
    Foo key = ...;
    return new SimpleImmutableEntry(key, barSubclassProvider);
  }
}

@Module
class MyProviderMapModule {
  @Provides
  static Map<Foo, Provider<Bar>> fooBarProviderMap(
      Set<Map.Entry<Foo, Provider<Bar>>> entries) {
    return ...;
  }
}

八、Activity注入

一般方法

对于Activity注入成员变量,因为Activity是系统创建的,所以需要在onCreate()中对已有的Activity实例进行注入,一般实现方式如下:

1.创建Application级别的Component,自定义Builde来提供绑定Application的方法,以及对目标Activity注入的方法:

@Component(modules = {MainActivityModule.class}) // 也可以包含自定义的Application级别的Module,此处省略不表
public interface AppComponent {
    // 自定义Builder,提供绑定Application的方法,@BindsInstance表示把所注解方法的参数绑定到Component当中
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder bindApplication(Application application);

        AppComponent build();
    }
    // 注入MainActivity,此处为请求依赖的新用法,传入已有的MainActivity实例,虽然已有实例,但是Dagger2会自动查找实例内@Inject的成员变量,满足其依赖
    void inject(MainActivity activity);
}

2.MainActivity里声明有个依赖需要注入:

public class MainActivity extends AppCompatActivity {
    @Inject
    String str;
    ...
}

3.创建对应的Module提供依赖:

@Module
public class MainActivityModule {
    @Provides
    String provideSomeString() {
        return "just some strings";
    }
}

4.Application中对AppComponent进行初始化:

public class MyApplication extends Application {

    private AppComponent mAppComponent;

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

        mAppComponent = DaggerAppComponent.builder().bindApplication(this).build();
    }

    public AppComponent getAppComponent() {
        return mAppComponent;
    }

}

5.MainActivity中就可以实现依赖注入了:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Inject
    String str;

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

        ((MyApplication)getApplication()).getAppComponent().inject(this);
        Log.e(TAG, "onCreate: " + str);
    }
}

但是这种做法有两个缺点。一是每个需要注入的Activity都需要复制粘贴这段代码;二是违反了依赖注入中依赖需求方不应该知道依赖的来源这一原则,如果这样操作耦合性就很高了。

所以Dagger2专门为Android提供了一个系统组件的注入框架,需要依赖以下库:

compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'

实现步骤:

1.定义一个Application级别的Component,把AndroidInjectionModule作为一个module,提供一个方法注入Application:

@Component(modules = {AndroidInjectionModule.class})
public interface AppComponent {
    void inject(MyApplication application);
}

2.创建一个继承了AndroidInjector<目标Activity>接口的Subcomponent,里面包含一个继承了AndroidInjector.Builder<目标Activity>的Builder抽象类,这个Subcomponent同样把AndroidInjectionModule作为一个module

@Subcomponent(modules = AndroidInjectionModule.class)
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity>{}
}

3.然后把这个Subcomponent定义为对应ActivityModule的Subcomponent,对应ActivityModule中提供一个方法,接收Subcomponent.Builder为参数,返回AndroidInjector.Factory<? extends Activity>

@Module(subcomponents = MainActivitySubcomponent.class)
public abstract class MainActivityModule {
    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
}

4.Application级别的Component中把目标Activity的Module包含进来

@Component(modules = {AndroidInjectionModule.class, MainActivityModule.class})
public interface AppComponent {
    void inject(MyApplication application);
}

5.Application类实现HasActivityInjector接口,成员变量里请求一个类型为DispatchingAndroidInjector<Activity>的依赖,重写接口方法返回这个对象:

public class MyApplication extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.create().inject(this);
    }

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

6.最后在目标Activity的super.onCreate()前调用AndroidInjection.inject(this),即可完成依赖注入:

public class YourActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
  }
}

依赖注入流程是Activity中的AndroidInjection.inject(this)会调用Application中的androidInjector()获取到DispatchingAndroidInjector,然后调用DispatchingAndroidInjector的inject(activity)方法。

这个inject过程的话,是DispatchingAndroidInjector会去查找AndroidInjector.Factory(也就是目标ActivitySubcomponent.Builder),然后创建出AndroidInjector(也就是目标ActivitySubcomponent),然后把目标Activity传给AndroidInjector去调用inject(activity)完成注入。


参考:

Dagger2 official documents

introduction to dagger2 using dependency injection in android

the new dagger 2 android injector

android and dagger 2.10 androidinjector

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值