为了账号安全,请及时绑定邮箱和手机立即绑定

Dagger2从入门到补胎(二)

标签:
Android

使用

继续学习在代码中会看到的标注:@Named、@Qualifier、@Singleton、@Scope。这四个标注包括之前学的@Inject其实不是在dagger的包中,而是javax-inject包中:

5b77a04b0001add307180390.jpg

经过上篇的学习,结合@Inject、@Component、@Module、@Provides的使用,先看一个例子:

City.java


public class City {private String name;public City() {}public String show() {return name;}public void setName(String name) {this.name = name;}}

BeanModule.java


@Modulepublic class BeanModule {@ProvidesCity providerCityCD() {City city = new City();city.setName("成都");return city;}@ProvidesCity providerCityZG(){City city = new City();city.setName("自贡");return city;}}

MainActivityComponent.java


@Component(modules = BeanModule.class)public interface MainActivityComponent {void inject(MainActivity activity);}

MainActivity.java


public class MainActivity extends AppCompatActivity {public static String TAG = "hcy";@InjectCity city;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//in Android Studio, select Build > Rebuild ProjectDaggerMainActivityComponent.create().inject(this);Log.d(TAG, "city name: " + city.show());}}

一跑起来就GG了

5b77a04c00014f1618080354.jpg

心路历程是这样的,MainActivity.java作为依赖需求方,编译生成MainActivity_MembersInjector,BeanModule.java作为依赖提供方,编译生成两个Xx_Factory,那么问题来了,有两个“备胎”供选择,你选谁?Component一脸懵逼,愣是搞不懂把哪两个”绑定”起来,直接罢工了,因此可以看到上篇中的DaggerMainActivityComponent在这里没有生成。

5b77a04d0001836409880636.jpg

@Named

那么告诉Component我们要的是哪个不就行啦?可以使用@Named标注,改下BeanModule.java


@Modulepublic class BeanModule {@Named("CD")@ProvidesCity providerCityCD() {City city = new City();city.setName("成都");return city;}@Named("ZG")@ProvidesCity providerCityZG() {City city = new City();city.setName("自贡");return city;}}

MainActivity.java


public class MainActivity extends AppCompatActivity {public static String TAG = "hcy";@Named("CD")@InjectCity city;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//in Android Studio, select Build > Rebuild ProjectDaggerMainActivityComponent.create().inject(this);Log.d(TAG, "city name: " + city.show());

@Named(“参数”)和被引用的地方填的一致就可以了,编译运行就不会报错了。此时编译生成的文件:

5b77a04d000172fb10140742.jpg

熟悉的DaggerMainActivityComponent又回来了,而且两个依赖提供者也都在,通过打印我们知道选的是CD,那它是怎么实现的呢?看下DaggerMainActivityComponent的initialize():


private void initialize(final Builder builder) {this.providerCityCDProvider = BeanModule_ProviderCityCDFactory.create(builder.beanModule);this.mainActivityMembersInjector = MainActivity_MembersInjector.create(providerCityCDProvider);}

可以看到,依赖提供方和需求方通过@Named加了相同的标注CD后,在创建工厂的时候只会把CD这个提供方初始化,ZG根本没有露脸的机会,后面的流程就跟上篇一样了。

@Qualifier

使用@Qualifier可以实现@Named一样的功能,Qualifier翻译过来是修饰符的意思,先看下用法,再分析@Qualifier和@Named之间的“苟且”。添加一个接口CD.java和ZG.java


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

修改BeanModule.java如下:


@Modulepublic class BeanModule {@CD@ProvidesCity providerCityCD() {City city = new City();city.setName("成都");return city;}@ZG@ProvidesCity providerCityZG() {City city = new City();city.setName("自贡");return city;}}

MainActivity.java


public class MainActivity extends AppCompatActivity {public static String TAG = "hcy";@CD@InjectCity city;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//in Android Studio, select Build > Rebuild ProjectDaggerMainActivityComponent.create().inject(this);Log.d(TAG, "city name: " + city.show());}}

修改后和输出结果、编译生成的类以及源码都和@Named一毛一样。@Qualifier可以让我们自己创建限定符,其实@Named内部也是通过@Qualifier实现,源码如下:


/*** String-based {@linkplain Qualifier qualifier}.** <p>Example usage:** <pre>*   public class Car {*     &#064;Inject <b>@Named("driver")</b> Seat driverSeat;*     &#064;Inject <b>@Named("passenger")</b> Seat passengerSeat;*     ...*   }</pre>*/@Qualifier@Documented@Retention(RUNTIME)public @interface Named {/** The name. */String value() default "";}

从注释可知@Named是@Qualifier的String型实现;看下github上一张介绍图:

5b77a04e0001241307400376.jpg

顺便提一下这里的@Retention,翻译过来是保留的意思,它是用来指定我们自定义的限定符能保留的多久,有三种可选:


/*** Indicates how long annotations with the annotated type are to* be retained.  If no Retention annotation is present on* an annotation type declaration, the retention policy defaults to* {@code RetentionPolicy.CLASS}.*/public enum RetentionPolicy {/*** Annotations are to be discarded by the compiler.*/SOURCE,/*** Annotations are to be recorded in the class file by the compiler* but need not be retained by the VM at run time.  This is the default* behavior.*/CLASS,/*** Annotations are to be recorded in the class file by the compiler and* retained by the VM at run time, so they may be read reflectively.** @see java.lang.reflect.AnnotatedElement*/RUNTIME}

@Singleton

@Singleton从名字就知道是单例,怎么用这个?先看一个例子:

City.java


public class City {public City() {}}

BeanModule.java


@Modulepublic class BeanModule {@Named("CD")@ProvidesCity providerCityCD() {City city = new City();return city;}@Named("ZG")@ProvidesCity providerCityZG() {City city = new City();return city;}}

MainActivityComponent.java


@Component(modules = BeanModule.class)public interface MainActivityComponent {void inject(MainActivity activity);}

MainActivity.java


public class MainActivity extends AppCompatActivity {public static String TAG = "hcy";@Named("CD")@InjectCity cityCD1;@Named("CD")@InjectCity cityCD2;@Named("ZG")@InjectCity cityZG1;@Named("ZG")@InjectCity cityZG2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//in Android Studio, select Build > Rebuild ProjectDaggerMainActivityComponent.create().inject(this);Log.d(TAG, "cityCD1 is: " + cityCD1);Log.d(TAG, "cityCD2 is: " + cityCD2);Log.d(TAG, "cityZG1 is: " + cityZG1);Log.d(TAG, "cityZG2 is: " + cityZG2);}}

例子很简单,打印city如下:


D/hcy     (13129): cityCD1 is: com.hcy.huchengyang.dagger2.bean.City@35051924D/hcy     (13129): cityCD2 is: com.hcy.huchengyang.dagger2.bean.City@36acdc8dD/hcy     (13129): cityZG1 is: com.hcy.huchengyang.dagger2.bean.City@14db0e42D/hcy     (13129): cityZG2 is: com.hcy.huchengyang.dagger2.bean.City@28a95e53

可以看到这里获取到的对应4个不同的city,按照上一篇的分析套路可以很快定位到原因,查看编译生成的MainActivity_MembersInjector.java中的injectMembers()方法


@Overridepublic void injectMembers(MainActivity instance) {if (instance == null) {throw new NullPointerException("Cannot inject members into a null reference");}instance.cityCD1 = cityCD1AndCityCD2Provider.get();instance.cityCD2 = cityCD1AndCityCD2Provider.get();instance.cityZG1 = cityZG1AndCityZG2Provider.get();instance.cityZG2 = cityZG1AndCityZG2Provider.get();}

这里会调用目标工厂的get方法,最后调用到BeanModule的方法创建对象;如果要确保单例,@Singleton就可以派上用场了。

使用

1.在提供依赖的构造函数上加@Singleton


@Modulepublic class BeanModule {@Named("CD")@Provides@SingletonCity providerCityCD() {City city = new City();return city;}@Named("ZG")@ProvidesCity providerCityZG() {City city = new City();return city;}}

2.@Component标注的接口加上@Singleton


@Singleton@Component(modules = BeanModule.class)public interface MainActivityComponent {void inject(MainActivity activity);}

修改后打印:


D/hcy     (13519): cityCD1 is: com.hcy.huchengyang.dagger2.bean.City@35051924D/hcy     (13519): cityCD2 is: com.hcy.huchengyang.dagger2.bean.City@35051924D/hcy     (13519): cityZG1 is: com.hcy.huchengyang.dagger2.bean.City@36acdc8dD/hcy     (13519): cityZG2 is: com.hcy.huchengyang.dagger2.bean.City@14db0e42

那么它是怎么保证单例的呢?查看源码会发现MainActivity_MembersInjector.java中的injectMembers()方法和之前是一样的,但是DaggerMainActivityComponent.java的initialize()方法发生了改变:


private void initialize(final Builder builder) {this.providerCityCDProvider =DoubleCheck.provider(BeanModule_ProviderCityCDFactory.create(builder.beanModule));this.providerCityZGProvider = BeanModule_ProviderCityZGFactory.create(builder.beanModule);this.mainActivityMembersInjector =MainActivity_MembersInjector.create(providerCityCDProvider, providerCityZGProvider);}

这里多了DoubleCheck的操作。看下DoubleCheck.java


public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {private static final Object UNINITIALIZED = new Object();private volatile Provider<T> provider;private volatile Object instance = UNINITIALIZED;private DoubleCheck(Provider<T> provider) {assert provider != null;this.provider = provider;}@Overridepublic T get() {Object result = instance;if (result == UNINITIALIZED) {//第一次取值后下次再来判断不满足直接返回之前的实例synchronized (this) {result = instance;if (result == UNINITIALIZED) {result = provider.get();Object currentInstance = instance;if (currentInstance != UNINITIALIZED && currentInstance != result) {throw new IllegalStateException();}instance = result;provider = null;}}}return (T) result;}public static <T> Provider<T> provider(Provider<T> delegate) {checkNotNull(delegate);if (delegate instanceof DoubleCheck) {return delegate;}return new DoubleCheck<T>(delegate);}}

整个类都没多少代码,省略了注释和异常以及部分代码,DoubleCheck和我们的Factory都实现了共有接口Provider,保证单例的操作就是这里的get()方法,注释已解释,可以跟着流程走一波。这里看下github上的一张图,下图左边的对应没有标注@Singleton,每次取时都会重新new;右边有标注的在第一次创建时会缓存一份,下次再获取直接就返回缓存的。

5b77a04e00013a2408490435.jpg

@Scope

使用@Scope可以实现@Singleton一样的功能,Scope翻译过来是作用域的意思,其实@Singleton就是@Scope的实现(有点像上面@Named和@Qualifier的关系),源码如下:


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

我们可以完全可以自己定义一个接口如:MainActivityScope.java

修改MainActivityComponent.java


@MainActivityScope@Component(modules = BeanModule.class)public interface MainActivityComponent {void inject(MainActivity activity);}

修改BeanModule.java


@Modulepublic class BeanModule {@Named("CD")@Provides@MainActivityScopeCity providerCityCD() {City city = new City();return city;}@Named("ZG")@ProvidesCity providerCityZG() {City city = new City();return city;}}

效果是一样的,@Scope是成对使用的,在@Module的@Provides方法上使用@Scope标注,那么对应的@Component也需要@Scope标准,此时@Provides方法提供的依赖在@Component中为单例。

原文链接:http://www.apkbus.com/blog-822415-77548.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消