@Qualifier(限定符) 、 @Named
场景:假如在去仓库中寻找Fruit 实例的时候有两个@Provides ,并且return 的东西都是Fruit ,这个时候如果没有做好标识,那么就会出现异常:
![](6.png](http://upload-images.jianshu.io/upload_images/2304809-114089c99fa5e121.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这个时候就要用到限定符。
@Named是@Qualifier 的一个默认实现。推荐使用@Qualifier。
用法(以下代码都是基于第一篇中的。):
1.新建两个注解类:
@Qualifier
public @interface FruitApple {
}
@Qualifier
public @interface FruitPlum {
}
2.在FruitModule 中使用自定义的注解进行标识。
@Module
public class FruitModule {
@FruitApple
@Provides
public Fruit getApple() {
return new Apple();
}
@FruitPlum
@Provides
public Fruit getPlum(){
return new Plum() ;
}
}
3 . Fish 中也要指定Fruit 参数前指定注解。
public class Fish {
private Vegetables vegetables;
private Fruit fruit;
@Inject
Fish(Vegetables vegetables, @FruitApple Fruit fruit) {
this.vegetables = vegetables;
this.fruit = fruit;
}
public String getMaterialName(){
return vegetables.getName()+"-->"+ fruit.getName();
}
}
@Component下的 dependence 和@SubComponent
dependence 和 @SubComponent的使用:
1.如果想保持两个Component相对独立,并且想明确的显示两个Component之间的依赖关系,就用dependence
dependence eg:
还是上一个的例子(其他类不做改变)。假如Fish是来自第三方类库的(我们无法操作构造函数)。并且的我的Fruit和Vegetables 在其他类中也要声明实例(就是也要用单独的Component),那么我么们直接用dependencies 来之间建立Component之间的依赖关系,而不需要在重复指定modules了。如下
@Component(modules = FruitModule.class)
public interface FruitComponent {
@FruitApple Fruit getFruit();
}
@Component(modules = VegetablesModule.class)
public interface VegetableComponent {
//名字和inject 区分
Vegetables getVegetable();
}
然后在FishComponent 中建立依赖
@Component(modules =FishModule.class,dependencies = {FruitComponent.class,VegetableComponent.class})
public interface FishComponent {
//将实例注入目标
void inject(MainActivity mainActivity);
}
MainActivity 中完成注入。
DaggerFishComponent.builder()
.fishModule(new FishModule())
.fruitComponent(DaggerFruitComponent.create())
.vegetableComponent(DaggerVegetableComponent.create())
.build().inject(this);
@SubComponent eg:
@SubComponent 作用更加偏向于: A Component 只为B Component 才提供作用的,而自己本身并没有其他的独立作用,代码上也是只有在拿到A Component的时候才能拿到BComponent。 (就是A和B是紧密结合的),并且B Component 也不关心依赖着哪一个Component
- 其他的不变,只要改动Component
@Component(modules = FruitModule.class)
public interface FruitComponent {
VegetableComponent getVegetableComponent(VegetablesModule vegetablesModule);
}
@Subcomponent(modules = VegetablesModule.class)
//身为Subcomponent的我,并不知道我依赖着哪一个Component
public interface VegetableComponent {
FishComponent getFishComponet(FishModule fishModule);
}
@Subcomponent(modules = FishModule.class)
public interface FishComponent {
//将实例注入目标
void inject(MainActivity mainActivity);
}
- mainActivity 注入。
DaggerFruitComponent.create()
.getVegetableComponent(new VegetablesModule())
.getFishComponet(new FishModule())
.inject(this);
@Scope(限定域)和@Singleton
这个地方起始还是有点坑的。
@Singleton : 披着全局单例的皮,起始它并没有全局单例的能力。(能力和自定义的@Scope是一样,他只是事先定义好的。)
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
看看就知道(还是上面的例子):
1.先来和所有@Scope一样的用法(再@provides 和 @Component 加上@Singleton):
@Module
public class FishModule {
@Provides
@Singleton
public Fish getFish(Vegetables vegetables, @FruitApple Fruit fruit){
return new Fish(vegetables,fruit);
}
}
多增加一个Activity(用于测试是否全局单例)
@Singleton
@Component(modules = FishModule.class,dependencies = {FruitComponent.class,VegetableComponent.class})
public interface FishComponent {
//将实例注入目标
void inject(MainActivity mainActivity);
void inject(TestActivity testActivity);
}
再MainActivity 中测试两条鱼是否一样。
public class MainActivity extends AppCompatActivity {
// Activity 如有多个不同类的实例需要注入,那么也要在一个Component注入。
//不能使用多个Component。
@Inject
Fish fish;
@Inject
Fish fish2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerFishComponent.builder().vegetableComponent(DaggerVegetableComponent.create())
.fruitComponent(DaggerFruitComponent.create()).build().inject(this);
Button btn= (Button) findViewById(R.id.resultBtn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,TestActivity.class));
}
});
Log.e("TAG","------------------->"+fish);
Log.e("TAG","------------------->"+fish2);
//=====结果 :true========
btn.setText((fish==fish2)+"");
}
public class TestActivity extends AppCompatActivity {
@Inject
Fish fish3;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerFishComponent.builder().vegetableComponent(DaggerVegetableComponent.create())
.fruitComponent(DaggerFruitComponent.create()).build().inject(this);
Log.e("TAG","------------------->"+fish3);
}
}
![
]SKU]2GLEG]93.png]( http://upload-images.jianshu.io/upload_images/2304809-099eddf1370045aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可见只有在同一个DaggerFishComponent的实例之下,注入的Fish 才是一样的。
那为什么说他披着全局的皮呢?
Dagger 2也是建议将@Singleton 用于全局。而自定义@Scope用于局部。
1.代码的可读性。@Singleton 给人以看就可以知道是单例的。而那些常用的自定义@ActivityScope @FragmentScope (这是要自己声明的) 则是侧重于作用于Activity、Fragment范围内。便于可读,分别开来。
2。侧面说明:其他的@Scope 可以依赖于@Singleton。但是@Singleton 依赖于其他的@Scope编译上就会报错。Dagger2 也说它是生命周期最长的,生命周期长的无法以依赖于短的。(说明@Singleton得天独厚。哈哈。)
那么怎么获取全局单例?
只需要保证获取到的FishComponent是一样的。那么就将FishComponent的获取放在Application中,那个Activity要用,先先getApplication,再去获取唯一的FishComponent即可。
修改一下:
1.自定义一个@Scope
@Scope
public @interface ActivityScope {
}
2.给FishComponent、FishModule 注解
@ActivityScope
@Component(modules = FishModule.class,dependencies = {FruitComponent.class,VegetableComponent.class})
public interface FishComponent {
//将实例注入目标
void inject(MainActivity mainActivity);
void inject(TestActivity testActivity);
}
@Module
public class FishModule {
@ActivityScope
@Provides
public Fish getFish(Vegetables vegetables, @FruitApple Fruit fruit){
return new Fish(vegetables,fruit);
}
}
3 创建 AppComponent、AppModule
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApp myApp);
}
@Module
public class AppModule {
@Provides
@Singleton
public FishComponent getFishCompent(){
return DaggerFishComponent.builder().vegetableComponent(DaggerVegetableComponent.create())
.fruitComponent(DaggerFruitComponent.create()).build();
}
}
4 application 注入
public class MyApp extends Application {
@Inject
FishComponent fishComponent;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create().inject(this);
}
//
public FishComponent getFishComponent(){
return fishComponent;
}
}
5 Activity 中测试是否单例
public class MainActivity extends AppCompatActivity {
@Inject
Fish fish;
@Inject
Fish fish2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MyApp) getApplication()).getFishComponent().inject(this);
Log.e("TAG","------------------->"+fish);
Log.e("TAG","------------------->"+fish2);
}
}
public class TestActivity extends AppCompatActivity {
@Inject
Fish fish3;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MyApp) getApplication()).getFishComponent().inject(this);
Log.e("TAG","------------------->"+fish3);
}
}
输出的地址就是一样的了(不贴了)。
@Scope 跟最上面测试@Singleton一样的用法。我们一般是用来的定义局部的。