(十一) Dagger2 @IntoSet案例分析

代碼示例
// 父類
abstract class Animal {
    // 抽象方法
    abstract void sleep();
}
// 子類
class Tiger extends Animal {
    @Override
    public void sleep() {
        System.out.println("Tiger sleeping");
    }
}
// 子類
class Cat extends Animal {
    @Override
    public void sleep() {
        System.out.println("Cat sleeping");
    }
}
@Module
class ZooModule {
    @Singleton // Tiger對象保證單例
    @Provides
    @IntoSet // 將Tiger對象存入Set集合中
    public Animal providerTiger() {
        return new Tiger();
    }

    @Singleton // Cat對象保證單例
    @Provides
    @IntoSet // 將Cat對象存入Set集合中
    public Animal providerCat() {
        return new Cat();
    }
}
@Singleton // 保證ZooModule中的對象單例
@Component(modules = {ZooModule.class})
interface ZooComponent {
    void inject(Zoo zoo);
}
public class Zoo {
    @Inject
    Set<Animal> set0; 
    @Inject
    Set<Animal> set1; 
    @Test
    public void 案例十一() {
        DaggerZooComponent.create().inject(this);
        set0.forEach(new Consumer<Animal>() {
            @Override
            public void accept(Animal animal) {
                animal.sleep();
            }
        });
        set1.forEach(new Consumer<Animal>() {
            @Override
            public void accept(Animal animal) {
                animal.sleep();
            }
        });
    }
}
Dagger2生成代碼閱讀

主要分析如何生成一個Set集合

就着上面的案例來看下Degger2生成的代碼,生成的代碼在build\generated\sources\annotationProcessor\..文件夾中.

  • 分析下注入的邏輯,基本就能弄清楚.調用.inject(this)注入後的邏輯如下:
final class DaggerZooComponent implements ZooComponent {
    @Override
    public void inject(Zoo zoo) {
      injectZoo(zoo);
    }
    private Zoo injectZoo(Zoo instance) {
        // getSetOfAnimal()該方法注意
        Zoo_MembersInjector.injectSet0(instance, getSetOfAnimal());
        Zoo_MembersInjector.injectSet1(instance, getSetOfAnimal());
        return instance;
    }
    private Set<Animal> getSetOfAnimal() {
        // providerTigerProvider.get() 保證Tiger對象單例
        // providerCatProvider.get() 保證Cat對象單例
        // 前面有分析過
        // 主要看SetBuilder.<Animal>newSetBuilder(2).add().build()
        return SetBuilder.<Animal>newSetBuilder(2).add(providerTigerProvider.get()).add(providerCatProvider.get()).build();
    }
}
  • 分析如何創建Set<Animal>集合並添加元素
public final class SetBuilder<T> {
    // SetBuilder.<Animal>newSetBuilder(2)
    // 創建一個SetBuilder對象,estimatedSize代表集合長度
    public static <T> SetBuilder<T> newSetBuilder(int estimatedSize) {
        return new SetBuilder<T>(estimatedSize);
    }
    // SetBuilder構造方法
    private SetBuilder(int estimatedSize) {
      // 它創建了一個List集合
      contributions = new ArrayList<>(estimatedSize);
    }
    // .add(providerTigerProvider.get())
    // 該方法將獲取到的單例對象存入List集合中.
    public SetBuilder<T> add(T t) {
      contributions.add(checkNotNull(t, SET_CONTRIBUTIONS_CANNOT_BE_NULL));
      return this;
    }
    // .build()
    // 最後一步是將list集合中的元素轉化爲Set集合,到這裏就弄清楚@IntoSet註解所做的事情了.
    public Set<T> build() {
      switch (contributions.size()) {
        case 0:
          return Collections.emptySet();
        case 1:
          return Collections.singleton(contributions.get(0));
        default:
          return Collections.unmodifiableSet(new HashSet<>(contributions));
      }
    }
}

另外要說的是案例代碼中,@Singleton註解只能保證Zoo中不同Set<Animal>集合對象中的元素是相同的,而不能保證Set<Animal>對象是單例.

  • @Binds註解用法
    @Binds註解就是用來代替@Provides註解的,上面的案例代碼中使用的是@Provides註解爲注射器提供對象,下面講下如何用@Binds註解.
abstract class Animal {
    abstract void sleep();
}
class Tiger extends Animal {
    // 這裏需要在構造上增加@Inject註解,
    // 因爲Module中沒有使用@Provides註解,用戶沒有手動提供Tiger對象,
    // 此時就需要Dagger2來創建Tiger對象,現實中也可以使用其他的Module來提供對象也行.
    @Inject
    public Tiger() {
    }
    @Override
    public void sleep() {
        System.out.println("Tiger sleeping");
    }
}

class Cat extends Animal {
    @Inject
    public Cat() {
    }
    @Override
    public void sleep() {
        System.out.println("Cat sleeping");
    }
}
// 
@Module
abstract class ZooModule {
    @Singleton //該註解有效,依舊能保證Tiger對象在注射器證明週期中爲單例.
    @IntoSet
    // 近似看作@Provides註解
    @Binds 
    // 方法需要是抽象方法,providerTiger方法的入參必須是Animal接口或者抽象類的實現類類型.
    abstract Animal providerTiger(Tiger tiger);
    @Singleton //該註解有效,依舊能保證Cat對象在注射器證明週期中爲單例.
    @IntoSet
    @Binds
    abstract Animal providerCat(Cat cat);
}
@Singleton
@Component(modules = {ZooModule.class})
interface ZooComponent {
    void inject(Zoo zoo);
}
public class Zoo {
    @Inject
    Set<Animal> set0;
    @Inject
    Set<Animal> set1;
    @Test
    public void 案例十一() {
        DaggerZooComponent.create().inject(this);
        set0.forEach(new Consumer<Animal>() {
            @Override
            public void accept(Animal animal) {
                animal.sleep();
            }
        });
        set1.forEach(new Consumer<Animal>() {
            @Override
            public void accept(Animal animal) {
                animal.sleep();
            }
        });
    }
}

@Binds註解與@Provides註解還是有很多細微的差別的,我覺得配@Binds註解Dagger2使用起來會靈活很多.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章