Dagger2 入门有这篇就够了(晋级篇)

Dagger2 入门晋级篇,直奔主题吧!

一 :组件之间的依赖

  1. MainActivityModule 中Schoole的提供方法,需要参数Principal 的实例,但是MainActivityModule ,中并没有提供。可是AppComponent对应的AppModule有提供。于是我们让MainComponent依赖AppComponent,并在AppComponent中将Principal 实例提供出来。另外注入的时候也略有不同,具体看代码(提示:这些代码在Demo中的simplemvpmedo 的Module中哦)
@(个人博客)Module
public class MainActivityModule {
    private MainActivity mMainActivity;

    public MainActivityModule(MainActivity activity) {

        this.mMainActivity = activity;
    }
    //货架
    @Provides
    public MainActivity provideMainActivity() {
        return mMainActivity;//货架提供的商品
    }
    @Provides
    public Schoole provideSchoole(Principal principal) {
        return new Schoole(principal);
    }

}

MainActivityModule 相对应的MainComponent ,该MainComponent 依赖了AppComponent。然后我们在看下AppComponentded

/**
 * Created by zyg on 2016/11/8.
 * 供货商,交易商
 */
@Component(dependencies = AppComponent.class ,modules = MainActivityModule.class)
public interface MainComponent {
    void inject(MainActivity mainActivity);//签订合同,必须用MainActivity(采购商),接收。它老爸都不行哦
}
  • AppComponent 作为被依赖的控件,需要向依赖它的控件提供Principal 的实例,因为依赖它的组件MainComponent 需要Principal 的实例,然后再看AppComponent 对应的Module

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    void inject(MyApplication application);
    //父组件必须把该类的实例提供给子组件
    Principal getPrincipal();
}
  • 调用的时候也有区别,还是看代码,看看注入代码
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "daggerTest";
    @Inject
    Schoole mSchoole;
    @Inject
    Schoole mSchoole2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainComponent.builder()
                .appComponent(getComponent())
                .mainActivityModule(new MainActivityModule(this))
                .build()
                .inject(this);

    }

    //获取AppComponent
    private AppComponent getComponent() {
        return ((MyApplication) getApplication()).getAppComponent();
    }

    //更新UI的操作
    public void refeshUi() {

        Log.d(TAG, "更新ui");

    }
}
  • 注入的时候多了appComponent(),这个方法,需要传入AppComponent作为参数,这里我们从MyApplication中获取,看下MyApplication 的代码
public class MyApplication extends Application {
    @Inject
    Principal mPrincipal; //校长
    @Inject
    Principal vicePrincipal;//副校长
    AppComponent appComponent;
    @Override
    public void onCreate() {

        appComponent = DaggerAppComponent
                .builder()
                .appModule(new AppModule())
                .build();
        appComponent.inject(this);
        Toast.makeText(this, mPrincipal.say(), Toast.LENGTH_SHORT).show();

    }

    public AppComponent getAppComponent() {
        return appComponent;
    }
}
总结:这样MainComponent 就从AppComponent中拿到了Principal 的实例,并传给new Schoole方法,最终完成 Schoole类的实例注入到MainActivity的任务。提醒下AppComponent,一定得把Principal 提供出去,这是关键。

二: @SubComponent,子组件(公共组件)

  1. 如果有一个组件,是每次创建实例提供给别人,而恰好其他组件(有多个)里面有需要它,如果只有一个,我们就用依赖搞定啦。多个它就可以定义成子组件,谁需要在谁的组件里面加一下,具体看例子.(提示:Demo源码中的DaggerTest的Module哦)
  • 先来定义个子组件,注解@Subcomponent.
@PerActivity
@Subcomponent(modules = ComonModule.class)
public interface CommonComponent {
    Test4 getText4();
}
  • 在看与子组件相关的Module--ComonModule ,子组件提供Test4,Test4的代码就不贴了,在Demo中看吧。
@Module
public class ComonModule {
    @Provides
    public Test4 provideTest4() {
        return new Test4();
    }

}
  • 再看父组件ApplicationComponent ,定义个抽象方法getCommonComponent(),把该子组件提供出去。需要用到该子组件的组件都要提供这么一个抽象方法,把子组件提供出去。
@PerActivity
@Component(modules = AndroidModule.class)
public interface ApplicationComponent {
    void inject(DemoApplication application);
    LocationManager getLocationManager();
    CommonComponent getCommonComponent();
}
  • 父组件的Module-- *AndroidModule *
@Module
public class AndroidModule {
    private final DemoApplication application;

    public AndroidModule(DemoApplication application) {
        this.application = application;
    }

    @Provides
    @PerActivity
    public LocationManager provideLoctionManager() {
        return (LocationManager) application.getSystemService(LOCATION_SERVICE);
    }
}
  • 注入到DemoApplication中,先 调用ApplicationComponent 的getCommonComponent()方法,得到子组件,然后调用子组件的 getText4()方法,得到Text4的实例, 具体看代码
public class DemoApplication extends Application {
    @Inject
    LocationManager locationManager;
    private ApplicationComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerApplicationComponent.builder()
                .androidModule(new AndroidModule(this))
                .build();
    /* //Component0中用单例注解,Module中也用单例注解,并且只获取一个子Component,则可以保持单例
       CommonComponent commonComponent = component().getCommonComponent();
        Test4 test4 = commonComponent.getText4();
        Test4 test41 = commonComponent.getText4();*/
        //这种写法,即使Component中,Module中都用了单例注解,也无法实现单例
        Test4 test4 = component.getCommonComponent().getText4();
        Test4 test41 = component.getCommonComponent().getText4();
        component().inject(this);
        Toast.makeText(this, locationManager.getClass().getSimpleName(), Toast.LENGTH_SHORT).show();

    }

    public ApplicationComponent component() {
        return component;
    }
}

子组件的总结:

  • 子组件不能独立使用,必须依靠父组件才能向外提供实例。
  • 父组件必须提供抽象方法,将该子组件提供出去。然后才能拿到子组件,并获取子组件提供的类的实例。

三:单例的使用@Scope注解;

  1. 如何实现单例,依然是看代码
  • AppModule的代码:
import com.zhang.testing.simplemvpdemo.bean.Principal;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

/**
 * Created by zyg on 2016/11/15.
 */
@Module

public class AppModule {
    @Provides
    @Singleton
    public Principal providePrinciple() {
        return new Principal();
    }
}
  • AppComponent的代码
package com.zhang.testing.simplemvpdemo.di.component;

import com.zhang.testing.simplemvpdemo.MyApplication;
import com.zhang.testing.simplemvpdemo.di.module.AppModule;

import javax.inject.Singleton;

import dagger.Component;

/**
 * Created by zyg on 2016/11/15.
 */
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    void inject(MyApplication application);
}

注入代码就不贴了,看效果,实现了单例。那么如何实现单例??

  • 就是Module 中和 Component中都用@Singleton,注解(注意Module中应该注解在提供方法哪里),即必须成对出现才能实现单例
  • 如果Module 中没有注解,Component中可注解,可不注解,但是Module中提供了注解,Component中必须提供注解,否则编译无法通过,必须成对出现才能实现单例哦!重要事情说两遍了!!!
  • 如果某个类是用@Inject注解,为Component提供实例的,那么无法在该类内使用注解实现单例,只能在Module中的Provide方法里使用注解实现单例
image

2. 那么你要问了@Singleton,是神马??,其实@Singleton,只是Dagger2为给我们提供的默认单例注解,其实它靠的还是@Scope, 越来越糊涂了??有木有?不着急马上就会豁然开朗!看下@Singleton的代码

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

看到了吗?@Singleton里面有个@Scope的注解。现在还不是解释的时候。那我们也写个像@Singleton的注解,看看能不能用。我们自定义了个@OnlyInstance ,然后我们把上面代码的 AppComponent和AppModule中的@Singleton,全部替换为@OnlyInstance ,运行后发现和用@Singleton的效果一摸一样,同样实现了单例。那么说明了什么呢?

@Retention(RUNTIME)
@Scope
public @interface OnlyInstance {
}
  1. 我们自定义的注解@OnlyInstance 和Dagger2提供的注解@Singleton,效果相同。他们都能实现单例,因为中间都有@Scope这个注解。
  2. @Scope 是个能提供局部单例的注解,这个注释的意思就是作用域,在作用域内保持单例,同属于一个作用域,共用一个相同实例。这会与component的生命周期(不是整个应用)关联.只是Component的生命周期内哦!有了它我们就能自定义很多个可以提供单例的注解。为什么需要自定义很多个注解??
  • 因为各个组件Component要独立出来,而要独立出来就不能在同一个作用域,那么就不能使用同一个注解.当然如果两个不相关的Component之间,可以用相同的注解。
    • 但是如果两个Component是依赖关系则不能用相同的单例注解。
    • 那么问题来了,我们说@Scope,能让实例在Component生命周期内保持单例,那如果我想全局保持单例,与整个应用的生命周期一样,那么该怎么做呢?其实我们在上面说依赖的时候已经贴过代码,只是没有详细的去解释而已。
      • Test3注入MainActivity中需要传入参数LocationManager的实例,但是MainActivityComponent对应Module中并没有提供这个实例,那么只能依赖ApplicationComponent。先看Test3的代码
public class Test3 {
    private LocationManager locationManager;
    public Test3(LocationManager manager) {
        this.locationManager = manager;
    }
    public String sayName(){
        return "我是Test3";
    }
}

父组件的代码,注意这里用的单例注解是 @PerActivity

@PerActivity//当Module中使用了单例注解,Component中必须使用单例注解。
@Component(modules = AndroidModule.class)
public interface ApplicationComponent {
    void inject(DemoApplication application);
    LocationManager getLocationManager();//将LocationManager 提供给子控件
}

父组件对应的Module,注意这里用的单例注解是 @PerActivity

@Module
public class AndroidModule {
    private final DemoApplication application;
    public AndroidModule(DemoApplication application) {
        this.application = application;
    }
    @Provides
    @PerActivity// 这是自定义单例注解
    public LocationManager provideLoctionManager() {
        return (LocationManager) application.getSystemService(LOCATION_SERVICE);
    }  
}

在看子组件的代码注意这里的单例注解我们用的是@OnlyInstance,和父组件的不一样,如果一样会报错。

@OnlyInstance
@Component(dependencies = ApplicationComponent.class,modules = CModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

在看看与子控件相关的Module代码,这里Module我们并没有让Test3也是单例状态,所以Test3的注入不是单例的。但是我们的目的是看LocationManager 注入是不是单例的。

@Module
public class CModule {
    @Provides
    Test3 provdeText3(LocationManager locationManager){
      return   new Test3(locationManager);
    }
    /**
     * 依赖中单例的总结:
     * 单例有源头性,如果上游是单例接收必须单例。
     * 父类Component提供的是单例,子Component必须单例,
     * 但仅限于Component,子的Module不受影响
     * Component 和Module之间,Module 是单例,Component必须单例。
     * @Scope的一个注释,这个注释的意思就是作用域,
     * 在作用域内保持单例:同属于一个作用域,共用一个相同实例
     * 为什么要新增一个呢,因为各个组件需要独立出来,
     * 因此如果是依赖关系,则需要各自在不同的注释作用域里面
     * (所以自定义一个@OnlyInstance)
     * 子Component 和 父Component 作用域不能相同,必须各自在不同的作用域里
     * 但是 Component和 Module之间必须用相同的作用域,当然如果Module没有,Component就随意了
     * 然后我们又定义了一个@PerActivity
     * 我们将之前的@Singleton用新建的(自定义的@PerActivity)替换掉,
     * 验证两次的生成代码,发现一模一样,
     * @Singleton,只是Dagger2为我们提供的一个单例注解。我们可以根据自己的需求自定义自己的单例注解
     * 但是我们定义的时候要命名最好是有意义的,@PerActivity,我们希望它的作用是保持Activity内单例,见名知意吧!
     *
     * */
}

接下来我们再看下注入代码。这里applicationComponent,方法需要的参数applicationComponent的实例,我们是从DemoApplication中获取的。只有保证了applicationComponent的唯一性才能保证单例超出Component生命周期后依然有效。这也变相说明@Scope注解实现了Component内单例,与Component生命周期相关,而不是与整个应用生命周期相关。

public class MainActivity extends AppCompatActivity {
    @Inject
    Test3 test3;
    @Inject
    Test3 test2;
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main)
        DaggerMainActivityComponent.builder()
                .applicationComponent(((DemoApplication) getApplication()).component())
                .build()//applicationComponent方法需要的参数是从DemoApplication获取的,没有重新新建
                .inject(this);
        Toast.makeText(this, test2.sayName(), Toast.LENGTH_SHORT).show();
    }
}

然后我们看下效果图


image
  • 子组件提供的类的实例无法通过注解的方式直接实现单例,但是我们可以通过只获取一个子组件Component而实现单例。而且子组件和父组件可以使用相同的单例注解。具体看代码吧!
    • 子组件的代码
@PerActivity
@Subcomponent(modules = ComonModule.class)
public interface CommonComponent {
    Test4 getText4();
} 

子组件相对应的Module

@Module
public class ComonModule {
    @PerActivity
    @Provides
    public Test4 provideTest4() {
        return new Test4();
    }

}

调用时候只获取一个子Component,就能实现单例,代码如下

public class DemoApplication extends Application {
private ApplicationComponent component;
@Override
public void onCreate() {
    super.onCreate();
    component = DaggerApplicationComponent.builder()
            .androidModule(new AndroidModule(this))
            .build();
    CommonComponent commonComponent = component().getCommonComponent();
    Test4 test4 = commonComponent.getText4();
    Test4 test41 = commonComponent.getText4();
    component().inject(this);
    Toast.makeText(this, locationManager.getClass().getSimpleName(), Toast.LENGTH_SHORT).show();
}
public ApplicationComponent component() {
    return component;
}
}
dagger2-4

单例的总结:我们自定义单例注解的时候,最好每个自定义的名称都有意义,比如在Activity内保持单例,那么就定义为@perAcvitity.

四: @Scope是用来自定义单例注解的,那么还有没有其他的,可以自定义的呢?就喜欢你这好奇的性格,还真有,它就是@Qualifier。它是做什么的呢?看个栗子!

  • 假设有这么个类需要注入Test5,当传入的值不同时,它的属性就不同。但是他们都是Test5类,系统如何区分他们呢?他们没办法区分。于是我们就得告诉他们,哪个是哪个。。。。,我们这里先用Dagger2提供的一个注解@Named。它需要传入一个字符串,来区别不同的实例。
public class Test5 {
    private int mFlag;
    public Test5(int flag) {
        this.mFlag = flag;
    }
    public String signature() {
        if (mFlag == 1) {
            return "我是美女。。。";
        } else if(mFlag==0){
            return "我是帅哥";
        }else if(mFlag==2){
            return "我是男人";
        }else {
            return "我是女人";
        }
    }
}
  • 接着来看Module 里面怎么写的。
@Module
public class AndroidModule {
    private final DemoApplication application;

    public AndroidModule(DemoApplication application) {
        this.application = application;
    }

    @Provides
    @PerActivity
    public LocationManager provideLoctionManager() {
        return (LocationManager) application.getSystemService(LOCATION_SERVICE);
    }

    @PerActivity
    @Provides
    public Context provideContext() {
        return application.getApplicationContext();
    }
    @PerActivity
    @Provides
    @Named("boy")
    public Test5 provideBoy() {
        return new Test5(0);
    }
    @PerActivity
    @Provides
    @Named("girl")
    public Test5 provideGirle() {
        return new Test5(1);
    }
}
  • 在看看怎么注入的,注入的时候也要注解清楚,不然系统还是无法区分他们的。这样就完成注入了。
public class DemoApplication extends Application {
  
    @Inject
    @Named("boy")
    Test5 boy;
    @Inject
    @Named("girl")
    Test5 girl;
    private ApplicationComponent component;
    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerApplicationComponent.builder()
                .androidModule(new AndroidModule(this))
                .build();
    /* //Component0中用单例注解,Module中也用单例注解,并且只获取一个子Component,则可以保持单例
       CommonComponent commonComponent = component().getCommonComponent();
        Test4 test4 = commonComponent.getText4();
        Test4 test41 = commonComponent.getText4();*/
        //这种写法,即使Component中,Module中都用了单例注解,也无法实现单例
        Test4 test4 = component.getCommonComponent().getText4();
        Test4 test41 = component.getCommonComponent().getText4();
        component().inject(this);
        Toast.makeText(this, locationManager.getClass().getSimpleName(), Toast.LENGTH_SHORT).show();

    }

    public ApplicationComponent component() {
        return component;
    }
}
dagger2_9
  • 等等,我们不是说自定义注解的吗?是的,@Name(xx),是Dagger2提供的注解。我们看看它的代码。
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}
  • 我们自定义个几个试一试
    男孩
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ForBoy {
}

女孩

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ForGirl {
}
  • 然后我们用下试一试,直接上代码了哦!首先Module类,这里为了和原来的@Name区分开来,我把他们叫做男的和女的
@Module
public class AndroidModule {
    private final DemoApplication application;

    public AndroidModule(DemoApplication application) {
        this.application = application;
    }

    @Provides
    @PerActivity
    public LocationManager provideLoctionManager() {
        return (LocationManager) application.getSystemService(LOCATION_SERVICE);
    }

    @PerActivity
    @Provides
    public Context provideContext() {
        return application.getApplicationContext();
    }

    @PerActivity
    @Provides
    @Named("boy")//Dagger2提供的默认注解
    public Test5 provideBoy() {
        return new Test5(0);
    }

    @PerActivity
    @Provides
    @Named("girl")
    public Test5 provideGirle() {
        return new Test5(1);
    }
    @PerActivity
    @Provides
    @ForBoy//因传入的值不相同,所以属性不相同,而Dagger2无法区分,我们用注解告诉系统,他们是不同的,有区别的,
    public Test5 provideMan(){//因为Dagger2是以返回的类的名称来区分不同的返回方法的。
        return new Test5(2);
    }
    @PerActivity
    @Provides
    @ForGirl//我们自定义的注解
    public Test5 provideWoman(){
        return new Test5(3);
    }
}

再看注入的地方。

public class DemoApplication extends Application {
    @Inject
    LocationManager locationManager;
    @Inject
    @Named("boy")//注入到的地方也要有相应的注解,否则Dagger2还是无法区分的
    Test5 boy;
    @Inject
    @Named("girl")
    Test5 girl;
    @Inject
    @ForBoy//自定义的注解
    Test5 man;
    @Inject
    @ForGirl
    Test5 woman;

    private ApplicationComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerApplicationComponent.builder()
                .androidModule(new AndroidModule(this))
                .build();
    /* //Component0中用单例注解,Module中也用单例注解,并且只获取一个子Component,则可以保持单例
       CommonComponent commonComponent = component().getCommonComponent();
        Test4 test4 = commonComponent.getText4();
        Test4 test41 = commonComponent.getText4();*/
        //这种写法,即使Component中,Module中都用了单例注解,也无法实现单例
        Test4 test4 = component.getCommonComponent().getText4();
        Test4 test41 = component.getCommonComponent().getText4();
        component().inject(this);
        Toast.makeText(this, locationManager.getClass().getSimpleName(), Toast.LENGTH_SHORT).show();

    }

    public ApplicationComponent component() {
        return component;
    }
}
image
  • 另外,如果我们把boy,girl提供给依赖它的组件,那么提供方法里也要用相应的注解,否则报错的。具体看代码,一看就懂了。
@PerActivity
@Component(modules = AndroidModule.class)
public interface ApplicationComponent {
    void inject(DemoApplication application);
    LocationManager getLocationManager();

    CommonComponent getCommonComponent();
//提供给子组件使用的时候也要有相应的注解。不然怎么分辨呢
    @Named("boy")
    Test5 provideBoy();

    @Named("girl")
    Test5 provideGirl();

    @ForBoy
    Test5 provideMan();

    @ForGirl
    Test5 provideWoman();

}

就是这么自定义的,至于为什么要自定义,每次敲入不同的字符串你不烦吗?万一敲错了呢?那又怎么办!!

五:懒加载,这个比较简单。

  • Component和Module中的写法没任何变化,只是在注入的地方略有不同。用 Lazy<>
    把懒加载的类包裹起来,用的时候调用get方法获取类的实例
public class MainActivity extends AppCompatActivity {
    @ForGirl
    Lazy<Test5> beautifulGirl;//漂亮的女孩都比较懒
//注入的地方懒加载这么写。
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main)
        DaggerMainActivityComponent.builder()
                .applicationComponent(((DemoApplication) getApplication()).component())
                .build()
                .inject(this);
        Toast.makeText(this, beautifulGirl.get().signature(), Toast.LENGTH_SHORT).show();
        //注入完成后只是提供一个可以生成Test5的对象,通过get(),才能获得我们需要的真正实例
    }
}

六: 多绑定到Set中,一个Component可以有多个Module,同时不同Module中的相同数据类型的元素可以绑定到同一个Set中。这里我们列举了两种类型的Set,Set<String>和Set<Integer>

第一个ModuleA,第一个方法提供Set<String> 元素为:"ABC" ,第二个方法提供了Set<String>元素为:"abc"
第三个方法提供了Set<String>元素为:"DEF", "GHI" ,第四个方法提供了Set<Integer> 元素为:1

@Module
public class ModuleA {

    @Provides
    @IntoSet
    public String provideString(LocationManager locationManager) {
        return "ABC";
        //注入到Set<String>中,单个元素就用 @IntoSet
    }

    @Provides
    @IntoSet
    public String provideSecond(LocationManager locationManager) {
        return "abc";
        //注入到Set<String>中单个元素就用 @IntoSet
    }.

    @Provides
    @ElementsIntoSet
    public Set<String> provideStings(LocationManager manager) {
        return new HashSet<String>(Arrays.asList("DEF", "GHI"));
        //注入到Set<String>中,列表和多个元素用@ElementsIntoSet
    }
    @Provides
    @IntoSet
    public Integer provideInterger(LocationManager locationManager) {
        return 1;
        //注入到Set<Integer>中
    }
    /**
     * 这里需要注意的就是,在组件里面加入多个绑定的时候,
     * module的里面必须要有一个是@IntoSet 这个作为第一个标记,
     * 否则会出错,可以多个@IntoSet标记。
     如果是列表类型的,则使用@ElementsIntoSet就ok了。
     * */
}

ModuleB,这个Module提供了Set<String>的元素为:"def", "ghi" 和 Set<Integer> 元素为:3, 2

@Module
public class ModuleB {
    @Provides
    @ElementsIntoSet
    public Set<String> provideStings(LocationManager manager){
        return new HashSet<String>(Arrays.asList("def", "ghi"));
        //注入到Set<String>,多个元素用@ElementsIntoSet
    }
    @Provides
    @ElementsIntoSet
    public Set<Integer> provideIntegers(LocationManager manager) {
        return new HashSet<Integer>(Arrays.asList(3, 2));
        //注入到Set<Integer>中,因为是列表型所以 @ElementsIntoSet注解
    }
}

MainActivityComponent ,依赖了ApplicationComponent,注意:这里有个抽象方法 Set<String> getStrings();把Set<String> 提供出去的。具体什么用,我们待会说。

@OnlyInstance
@Component(dependencies = ApplicationComponent.class,modules = {ModuleA.class, ModuleB.class})
public interface MainActivityComponent {
    void inject(MainActivity activity);
    Set<String> getStrings();
}

现在就该到注入环节了。注入方式有两种哦

  1. 第一种注入方式,我们定义了相应类型的Set,接收注入。
public class MainActivity extends AppCompatActivity {
    @Inject
    Set<String> mStringSet;//注入的为Set<String>
    @Inject
    Set<Integer> integerSet;//注入的为 Set<Integer>
    @Inject
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainActivityComponent.builder()
                .applicationComponent(((DemoApplication) getApplication()).component())
                .build()
                .inject(this);
    }
}
image

如果我们在Component中移除ModuleB那么ModuleB中提供的相应元素就不会注入到相应Set中了,另外Dagger2会把数据类型相同的都注入到同一个Set中,提供类型为 Set<Integer>注入到integerSet里,提供类型为Set<String>mStringSet中。

  1. 第二种方式:还记得我们刚在MainActivityComponent 中提醒大家注意的那个抽象方法吗?第二个方法首先就是MainActivityComponent 中得定义一个抽象方法把Set<String>提供出来。这里我们只做个演示。所以就只做个Set<String> 的
public class MainActivity extends AppCompatActivity {
    Set<String> mStringSet;//注入的为Set<String>
    @Inject
    Set<Integer> integerSet;//注入的为 Set<Integer>
    @Inject
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mStringSet = DaggerMainActivityComponent
                .builder()
                .applicationComponent(((DemoApplication) getApplication()).component())
                .build()
                .getStrings();
       
    }
}

看效果图
image

多绑定到Map中,既然可以绑定到Set那么Map也不例外

首先看MyModule1 ,分别提供了几种类型的Map 同Set一样,Map会把Key类型一致,值类型一致的绑定到同一个Map中

@Module
public class MyModule1 {
    @Provides
    @IntoMap
    @StringKey("foo")
    static Long provideFooValue(){
        return 100L;
        //绑定到Map<String,Long>中
    }
    @Provides
    @IntoMap
    @StringKey("goo")
    static Long provideGooValue(){
        return 100L;
        //绑定到Map<String,Long>中
    }
    @Provides
    @IntKey(2)
    @IntoMap
    static int provideIntValue(){
        return 200;
        //绑定到Map<Integer Integer>中
    }
    @Provides
    @IntoMap
    @LongKey(1L)
    static Long provideLongValue(){
        return 100L;
        //绑定到Map<Long,Long> 中
    }
    @Provides
    @IntoMap
    @ClassKey(Test3.class)
    static String provideTest3Value(){
        return "Value for Test3";
        //key为Test3.class,用的是Dagger2的标注,所以key为Class<?>,值为String的Map<Class<?>,Sting>
    }
}

在MyModule1 中,我们看到各种类型的@MapKey注解的给中MapKey。随便看个 @ClassKey的源码

@Beta
@Documented
@Target(METHOD)
@MapKey
public @interface ClassKey {
  Class<?> value();
}

在MyModule2 中我们也自定义几个@MapKey的注解

@Module
public class MyModule2 {
    @Provides
    @IntoMap
    @MyEnumKey(MyEnum.ABC)
    public String provideABCValue() {
        return "Value for ABC";
        //绑定到 Map<MyModule2.MyEnum, String> 中去
    }

    @Provides
    @IntoMap
    @MyNumberClassKey(BigDecimal.class)
    public String provideNumberClassValue() {
        return "BigDecimal Value";
        //绑定到Map< Class<? extends Number>,String>中
    }

    //自定义枚举
    public static enum MyEnum {
        ABC, DEF;
    }

    //自定义注解枚举类型的@MapKey
    @MapKey
    @interface MyEnumKey {
        MyEnum value();
    }

    //自定义注解 @MapKey
    @MapKey
    @interface MyNumberClassKey {
        Class<? extends Number> value();
    }
}

然后我们看MainActivityComponent 的代码,同样,这里也有个抽象方法 Map<String, Long> getMap(); 把 Map<String, Long>的提供出去。也就是说 Map和Set一样。也有两种注入方式。这里我们只看一种。另一种和Set一样。

@OnlyInstance
@Component(dependencies = ApplicationComponent.class,modules = {ModuleA.class, ModuleB.class,MyModule1.class, MyModule2.class,CModule.class})
public interface MainActivityComponent {
    void inject(MainActivity activity);
    Set<String> getStrings();
    Map<String, Long> getMap();
}

看注入的地方代码

public class MainActivity extends AppCompatActivity {
    @Inject
    Test3 test3;
    @Inject
    Test3 test2;
    @Inject
    Test2 test21;//注入这个需要提供Set<String>为参数
    @Inject
    Set<String> mStringSet;//注入的为Set<String>
    @Inject
    Map<Integer, Integer> mIntegerIntegerMap;//Key Integer,值为Integer
    @Inject
    Map<String, Long> mapTest;//Key为String,值为Long的就会注入到这个里面
    @Inject
    Map<Long, Long> mapLongkey;//key 为Long,值为Long的就会注入到这个里面
    @Inject
    Map<MyModule2.MyEnum, String> myEnumStringMap;
    @Inject
    Map<Class<? extends Number>, String> classStringMap;//自定义Key
    @Inject
    Map<Class<?>, String> test3StringMap;//key为Test3.class,用的是Dagger2的标注,所以key为Class<?>
    @Inject
    Set<Integer> integerSet;//注入的为 Set<Integer>
    @Inject
    @Named("boy")
    Test5 boy;
    @Inject
    @Named("girl")
    Test5 girl;
    @Inject
    @ForBoy
    Test5 man;
    @Inject
    @ForGirl
    Test5 woman;
    @Inject
    @ForGirl
    Lazy<Test5> beautifulGirl;//漂亮的女孩都比较懒


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     /*   MainActivityComponent mainActivityComponent = DaggerMainActivityComponent
                .builder()
                .applicationComponent(((DemoApplication) getApplication()).component())
                .build();
        mStringSet = mainActivityComponent
                .getStrings();*/

        DaggerMainActivityComponent.builder()
                .applicationComponent(((DemoApplication) getApplication()).component())
                .build()
                .inject(this);
        Toast.makeText(this, test2.sayName(), Toast.LENGTH_SHORT).show();
        Toast.makeText(this, beautifulGirl.get().signature(), Toast.LENGTH_SHORT).show();
        //注入完成后只是提供一个可以生成Test5的对象,通过get(),才能获得我们需要的真正实例
    }
}
image

Map和Set的原理是一样的,只是Map的key可以通过自定义的方式来定义。对着撸一把。那么Demo地址:DaggerTest

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容