吃火锅版Dagger2
来源:互联网 发布:python 爬虫 股票数据 编辑:程序博客网 时间:2024/06/08 10:13
前言 、去吃火锅了
1.先定义好4个类:Meat.Vegetable,Seafood,Soup
2.定义Hotpat类,里面有个方法叫makeDinner(),这个方法需要上面的4个参数,这4个参数在Hotpot构造函数中创建.
public class Meat { public Meat(){ KLog.d("肉类"); }}
public class Seafood { public Seafood() { KLog.d("海鲜"); }}
public class Vegetable { public Vegetable() { KLog.d("蔬菜"); }}
public class Soup { String spicy;//辣的 public Soup() { KLog.d("清淡的锅底"); } public Soup(String spicy) { this.spicy = spicy; KLog.d("加辣的锅底"); }}
public class Hotpot { private final Soup soup; private final Meat meat; private final Vegetable vegetable; private final Seafood seafood; public Hotpot() { soup = new Soup(); meat = new Meat(); vegetable = new Vegetable(); seafood = new Seafood(); makeDinner(soup,meat,vegetable,seafood); } private void makeDinner(Soup soup, Meat meat, Vegetable vegetable, Seafood seafood) { KLog.d("吃火锅了" ); }}
简单易懂的写法,火锅制作出来了。
一、我们来看看用Dagger2的写法。
按照国际惯例,先给出配置:
compile 'com.google.dagger:dagger:2.+' annotationProcessor 'com.google.dagger:dagger-compiler:2.+'
- 增加一个类HotpotModule
首先声明:前面写的四个被依赖的类(Soup、Meat、 Vegetable、Seafood),不用做任何改动
下面要用到两个关键的注解@Module、@Provides
@Modulepublic class HotpotModule { @Provides public Meat provideMeat(){ return new Meat(); } // 返回值(被依赖的类类型) // 方法名(provideXxx必须以provide开头,后面随意) @Provides public Soup provideSoup(){ return new Soup(); } @Provides public Vegetable provideVegetable(){ return new Vegetable(); } @Provides public Seafood provideSeafood(){ return new Seafood(); }}
2.增加一个接口HotpotComponent
@Component(modules = {HotpotModule.class})public interface HotpotComponent { //注意:下面这三个方法,返回值必须是从上面指定的依赖库HotpotModule.class中取得的对象 //注意:而方法名不一致也行,但是方便阅读,建议一致,因为它主要是根据返回值类型来找依赖的 //★注意:下面这三个方法也可以不写,但是如果要写,就按照这个格式来 //但是当Component要被别的Component依赖时, //这里就必须写这个方法,不写代表不向别的Component暴露此依赖 Meat provideMeat(); Soup provideSoup(); Vegetable prorideVegetable(); Seafood provideSeafood(); //注意:下面的这个方法,表示要将以上的四个依赖注入到某个类中 //这里我们把上面的三个依赖注入到Hotpot中 void inject(Hotpot hotpot);}
- 重写Hotpat
public class Hotpot { //不能有修饰符 @Inject Soup soup; @Inject Meat meat; @Inject Vegetable vegetable; @Inject Seafood seafood; public Hotpot() { // DaggerHotpotComponent编译时才会产生这个类, // 所以编译前这里报错不要着急(或者现在你先build一下) DaggerHotpotComponent.builder() .hotpotModule(new HotpotModule()) .build() .inject(this); makeDinner(soup,meat,vegetable,seafood); } private void makeDinner(Soup soup, Meat meat, Vegetable vegetable, Seafood seafood) { KLog.d("吃火锅了" ); }}
测试:
12-25 13:59:57.545 6206-6206/com.example.myapplication D/lzx: [ (Soup.java:14)#<init> ] 清淡的锅底12-25 13:59:57.545 6206-6206/com.example.myapplication D/lzx: [ (Meat.java:12)#<init> ] 肉类12-25 13:59:57.545 6206-6206/com.example.myapplication D/lzx: [ (Vegetable.java:12)#<init> ] 蔬菜12-25 13:59:57.546 6206-6206/com.example.myapplication D/lzx: [ (Seafood.java:12)#<init> ] 海鲜12-25 13:59:57.546 6206-6206/com.example.myapplication D/lzx: [ (Hotpot.java:30)#makeDinner ] 吃火锅了
二、带参数怎么办
如果被依赖类的构造函数带有参数,要把这个参数的类型也管理起来
吃火锅怎能没有啤酒Beer呢,但是Beer的构造函数里需要传入一个Cup杯子,那么如何注入这个Beer呢
1.新增两个类
public class Cup { public Cup() { KLog.d("酒杯"); }}
public class Beer { Cup cup; public Beer(Cup cup) { this.cup = cup; KLog.d("酒杯里的啤酒"); }}
2.修改HotpotModule里的依赖
@Modulepublic class HotpotModule { ... //引入构造函数带参数的依赖 @Provides public Beer provideBeer(Cup cup){ return new Beer(cup); } @Provides public Cup provideCup(){ return new Cup(); }}
3.修改Component
@Component(modules = {HotpotModule.class})public interface HotpotComponent { //Beer provideBeer(Cup cup);//★注意:这里千万不能带参数,否则报错 Beer provideBeer(); Cup provideCup(); //注意:下面的这个方法,表示要将以上的四个依赖注入到某个类中 //这里我们把上面的三个依赖注入到Hotpot中 void inject(Hotpot hotpot);}
4.在目标类Hotpot里注入依赖
public class Hotpot { //不能有修饰符 @Inject Soup soup; @Inject Meat meat; @Inject Vegetable vegetable; @Inject Seafood seafood; @Inject Beer beer; //@Inject Cup cup; //如果需要用到Cup,这里才需要注入 public Hotpot() { // DaggerHotpotComponent编译时才会产生这个类, // 所以编译前这里报错不要着急(或者现在你先build一下) DaggerHotpotComponent.builder() .hotpotModule(new HotpotModule()) .build() .inject(this); makeDinner(soup, meat, vegetable, seafood, beer); } private void makeDinner(Soup soup, Meat meat, Vegetable vegetable, Seafood seafood, Beer beer) { KLog.d("吃火锅配啤酒"); }}
测试:
12-25 14:39:04.573 7496-7496/com.example.myapplication D/lzx: [ (Soup.java:14)#<init> ] 清淡的锅底12-25 14:39:04.573 7496-7496/com.example.myapplication D/lzx: [ (Meat.java:12)#<init> ] 肉类12-25 14:39:04.573 7496-7496/com.example.myapplication D/lzx: [ (Vegetable.java:12)#<init> ] 蔬菜12-25 14:39:04.573 7496-7496/com.example.myapplication D/lzx: [ (Seafood.java:12)#<init> ] 海鲜12-25 14:39:04.573 7496-7496/com.example.myapplication D/lzx: [ (Cup.java:12)#<init> ] 酒杯12-25 14:39:04.573 7496-7496/com.example.myapplication D/lzx: [ (Beer.java:14)#<init> ] 酒杯里的啤酒12-25 14:39:04.574 7496-7496/com.example.myapplication D/lzx: [ (Hotpot.java:32)#makeDinner ] 吃火锅配啤酒
三、有多个构造函数,怎么办
其实在前面的Soup类中已经设置了埋点,我们把汤底分成清淡的、辛辣的 。我们可以用Dagger提供的@Qualifier限定符来解决这个问题。
@Named(“String”)也能解决这个问题,只不过,传递的值只能是字符串,用@Qualifier更灵活一点
1.自己定义限定符,区分是哪个构造函数的
/** * 自定义一个限定符 */@Qualifier//限定符@Documented@Retention(RetentionPolicy.RUNTIME)public @interface Type { String value() default "";//默认值为""}
- 修改HotpotModeule,使用限定符@Type来区分不同的构造函数new出来的对象。
@Modulepublic class HotpotModule { ... @Type("normal") @Provides public Soup provideNormalSoup() { return new Soup(); } @Type("spicy") @Provides public Soup provideSpicySoup(String spicy) { return new Soup(spicy); } //由于我们的Soup构造函数里使用了String,所以这里要管理这个String(★否则报错) //int等基本数据类型是不需要这样做的 @Provides public String provideString(){ return new String(); }}
3.修改HotpotComponent
@Component(modules = {HotpotModule.class})public interface HotpotComponent { ... @Type("normal") Soup provideNormalSoup(); @Type("spicy") Soup provideSpicySoup(); String provideString();}
4.在目标类Hotpot里注入Soup依赖,我们要分别注入两个构造函数new出的对象
public class Hotpot { //不能有修饰符 @Inject @Type("normal") Soup normalSoup; @Inject @Type("spicy") Soup spicySoup; @Inject Meat meat; @Inject Vegetable vegetable; @Inject Seafood seafood; @Inject Beer beer; //@Inject Cup cup; //如果需要用到Cup,这里才需要注入 public Hotpot() { // DaggerHotpotComponent编译时才会产生这个类, // 所以编译前这里报错不要着急(或者现在你先build一下) DaggerHotpotComponent.builder() .hotpotModule(new HotpotModule()) .build() .inject(this); makeDinner(normalSoup ,spicySoup, meat, vegetable, seafood, beer); } private void makeDinner(Soup soup, Soup spicySoup, Meat meat, Vegetable vegetable, Seafood seafood, Beer beer) { KLog.d("吃鸳鸯火锅配啤酒"); }}
测试:
12-25 15:27:38.117 8880-8880/com.example.myapplication D/lzx: [ (Soup.java:14)#<init> ] 清淡的锅底12-25 15:27:38.117 8880-8880/com.example.myapplication D/lzx: [ (Soup.java:21)#<init> ] 加辣的锅底12-25 15:27:38.117 8880-8880/com.example.myapplication D/lzx: [ (Meat.java:12)#<init> ] 肉类12-25 15:27:38.118 8880-8880/com.example.myapplication D/lzx: [ (Vegetable.java:12)#<init> ] 蔬菜12-25 15:27:38.118 8880-8880/com.example.myapplication D/lzx: [ (Seafood.java:12)#<init> ] 海鲜12-25 15:27:38.118 8880-8880/com.example.myapplication D/lzx: [ (Cup.java:12)#<init> ] 酒杯12-25 15:27:38.118 8880-8880/com.example.myapplication D/lzx: [ (Beer.java:14)#<init> ] 酒杯里的啤酒12-25 15:27:38.118 8880-8880/com.example.myapplication D/lzx: [ (Hotpot.java:46)#makeDinner ] 吃鸳鸯火锅配啤酒
四、想要单例模式怎么办
1.在HotpotModule里,在provideXXX方法前添加@Singleton即可:
@Modulepublic class HotpotModule { @Singleton @Provides public Meat provideMeat() { return new Meat(); } ...}
2.在HotpotComponent里,在接口上添加@Singleton 注释 即可
@Singleton //注意是在接口上添加,注意位置@Component(modules = {HotpotModule.class})public interface HotpotComponent { ...}
3.Hotpot类不变,测试:
public class Hotpot { @Inject Meat meat; @Inject Meat meat2; ... private void makeDinner(Soup soup, Soup spicySoup, Meat meat, Vegetable vegetable, Seafood seafood, Beer beer) { KLog.d("吃鸳鸯火锅配啤酒"); KLog.d("meat:"+meat.toString()+",meat2:"+meat2.toString()); }}
结果证实是单例的!
12-25 15:37:12.738 9148-9148/com.example.myapplication D/lzx: [ (Hotpot.java:50)#makeDinner ] meat:com.example.myapplication.hot_pot.Meat@7513d90,meat2:com.example.myapplication.hot_pot.Meat@7513d90
五、 @Scope注解
dagger2中的Singleton其实就是Scope,可以查看Dagger2源码中Singleton的定义:
@Scope@Documented@Retention(RUNTIME)public @interface Singleton {}
自定义的@Scope:
@Documented@Scope@Retention(RetentionPolicy.RUNTIME)public @interface ApplicationScoped {}
Dagger2中@Singleton和自己定义的@ApplicationScoped代码上并没有什么区别,那为什么还要这么做呢? 是因为Dagger2需要保证Component和Module是匹配的,就需要用到这个注解。
那它是怎么做到单例的呢?查看DaggerXXXComponent中的initialize()方法,有@Scope类注解的@Provider生成的代码,外层多了一层DoubleCheck.provider(…);没有@Scope类注解的则是直接create一个新的实例。
private void initialize(final Builder builder) { this.provideMeatProvider = DoubleCheck.provider(HotpotModule_ProvideMeatFactory.create(builder.hotpotModule)); this.hotpotModule = builder.hotpotModule; }
简单来说就是加了@Scope的Provider,Dagger会缓存一个实例在Dagger…Component中,在Dagger…Component中保持单例,缓存的provide跟随Dagger…Component的生命周期,Dagger…Component被销毁时,provider也被销毁,这就是局部单例的概念,假如你的Dagger…Component是在你应用的application中(因为Application只实例化一次!一次!一次!),则就形成了全局单例。
最面我再来介绍Component之间的依赖关系。
六、Component之间的依赖
继续回到吃火锅的剧情:吃完火锅后好需要吃些水果助消化。假如这个水果的依赖体系已经完成了(商家早就准备好的),现在火锅需要这个体系的一个对象作为依赖。这个水果的体系如下:
public class Fruit { public Fruit() { KLog.d("饭后水果"); }}
@Modulepublic class FruitModule { @Provides public Fruit provideFruit(){ return new Fruit(); }}
@Component(modules = {FruitModule.class})public interface FruitComponent { // ★前面说过这里的这个方法是可以不写的, // 但是,如果你想让别的Component依赖这个Component, // 就必须写,不写这个方法,就意味着没有向外界,暴露这个依赖 Fruit provideFruit(); //void inject(Object o);//这里的水果并没有想注入到那个类中,可以不写inject方法}
那么FruitComponent和HotpotComponent是怎么联系起来的呢?
@Component(modules = {HotpotModule.class},dependencies = {FruitComponent.class})//引入FruitComponentpublic interface HotpotComponent { //其他的代码不改动}
在Hotpot注入依赖。
public class Hotpot { ... @Inject Fruit fruit; public Hotpot() { // DaggerHotpotComponent编译时才会产生这个类, // 所以编译前这里报错不要着急(或者现在你先build一下) //必须先执行这个 FruitComponent fruitComponent = DaggerFruitComponent.builder().fruitModule(new FruitModule()).build(); DaggerHotpotComponent.builder() .hotpotModule(new HotpotModule()) .fruitComponent(fruitComponent) // 在这里通过传入“fruitComponent(fruitComponent)”构建HotpotComponent, .build() .inject(this); makeDinner(normalSoup, spicySoup, meat, vegetable, seafood, beer, fruit); } private void makeDinner(Soup soup, Soup spicySoup, Meat meat, Vegetable vegetable, Seafood seafood, Beer beer, Fruit fruit) { KLog.d("吃鸳鸯火锅配啤酒"); KLog.d("吃饭后还有水果吃哦"); }}
测试:
12-25 17:34:58.026 12142-12142/com.example.myapplication D/lzx: [ (Hotpot.java:57)#makeDinner ] 吃鸳鸯火锅配啤酒12-25 17:34:58.026 12142-12142/com.example.myapplication D/lzx: [ (Hotpot.java:58)#makeDinner ] 吃饭后还有水果吃哦