前言
网上都说Dagger2是比较难上手的,我在看了大量资料和使用时也遇到了很多不懂或者模糊的知识点,而且大部分博客资料都比较古老。突然有那么一瞬间,突然明白了所以然,故总结了4篇文章。话说在java中使用还是很繁琐的,不要怕带你真正上手,并运用到我们的Android项目中去。
本次Dagger2讲解总共分4篇:
1、Dagger2基础知识及在Java中使用(1)
2、Dagger2基础知识及在Java中使用(2)
3、Dagger2进阶知识及在Android中使用
4、Dagger2华丽使用在MVP框架中
通过第一篇文章的学习,已经对Dagger2大致了解了。接下来这篇在Java里的使用。主要讲一些其他标注。注重的是细节
还是老样子,博客贴的代码,有部分省略,只为便于理解。
1、@Named的用法
它的用法有点像Tag,提前透露下,@Named是@Qualifier的一种。这里我们举个例:假如我们有个生成狗的机器。通过传入不同type生成不同的狗狗
狗狗的类
public class Dog {
private String Type;
public Dog(String type) {
this.Type = type;
}
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
}
接下来是Module,意思用@Named标识2种不同的初始化路径。假如这个时候不使用@Named的话,你运行的会报错,报错。Dagger2不知道,到底使用哪个
@Module
public class DogModule {
@Named("tag_1")
@Provides
Dog providesJinDog(){
return new Dog("金毛");
}
@Named("tag_2")
@Provides
Dog providesEhaDog(){
return new Dog("二哈");
}
}
Component还是正常的,还是贴下代码吧
@Component(modules = DogModule.class)
public interface DogComponent {
void inject(IstudyActivity istudyActivity);
}
Activity里的使用
public class IstudyActivity extends BaseActivity {
@Inject
@Named("tag_1")
Dog dog1;
@Inject
@Named("tag_2")
Dog dog2;
@Override
protected void processLogic() {
DaggerDogComponent.create().inject(this);
LogUtils.i("看看狗的种类",dog1.getType());
LogUtils.i("看看狗的种类",dog2.getType());
}
}
看看运行的结果,知道@Named的用法了吧
2、@Qualifier的用法
@Qualifier是限定符,它的作用很像函数的重载。之前说了@Named是@Qualifier的一种。看下@Named的源码:
是不是清晰了。这里我们自定义一个自己的Named
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface MyQualifier {
String value() default "";
}
这样就好了。接下来使用就和@Named一模一样。直接使用我们的标注@MyQualifier就行了。
当然你也可以不要String value() default “”;方法。那么久没有Tag了。只能标识一个
接下来说个比较特殊的,直接贴Module代码。他可以初始化数据。没错!牛逼!呸呸呸,是强悍。
@Module
public class DogOtherModule {
@MyQualifier("tag_1")
@Provides
String providesChina(){
return "中华田园犬";
}
@MyQualifier("tag_2")
@Provides
Dog providesfun(@MyQualifier("tag_1")String dogType){
return new Dog(dogType);
}
}
3、@Singleton & @Scope 的用法
首先我们看看@Singleton的源码,这么明显,@Singleton是@Scope的一种
@Scope //注明是Scope
@Documented //标记文档提示
@Retention(RUNTIME) //运行时候级别
public @interface Singleton {}
在接下来的讲解,我想先说下概念:@Singleton 是作用域单例,是作用域单例,是作用域单例(也可以理解为生命周期。Component在哪build就跟那个类的生命周期绑定,也就说只在那个绑定的实例类里起作用)
- 1、Component在Activity里build,那么这个单例只限于在这个Activity里。意思退出这个Activity,再进这个Activity时,这个单例会被重新new
- 2、如果Component在Application里build,那么这个单例就是全局的。注意这里涉及到了我们之前的Component依赖Component。
简单使用介绍:
1、不适用Module的局域单例:
不使用Module,构造函数当然要加上@Inject。然后在要单例的类上加上@Singleton标注;
接下来在Component里:在@Component上加上@Singleton。做好这些,在Activity里使用就和之前一样。但是此时已经是局域单例了
2、使用Module的情况:
使用Module有点类似使用第三库,你需要单例的类,什么都不需要加。记住是什么标注都别加。然后在Module的@Provides上加上@Singleton,然后在@Component上加上@Singleton。即可,也是局域单例
这里稍微来个例子,定义个Children小孩类:
public class Children {
}
对应Module如下
@Module
public class ActivityModule {
@Singleton
@Provides
Children ProvidesChildren(){
return new Children();
}
}
对应Component如下
@Singleton
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(KstudyActivity kstudyActivity);
}
做完一上操作就好了。在Activity里的操作和普通的@Inject一样。此时chidren就在KstudyActivity是局域单例了。意思退出KstudyActivity页面再重新进入KstudyActivity页面,它会被重新实例化,和之前的chidren不是一个,它被重新new了。
实现全局单例
这里为了更加理解Dagger2,这个时候我打算把局域单例,和全局单例放在一起,放在一个页面展示,并打印他们的HashCode值。这样更加能比较出来,看出区别。之前的局域单例代码留着,先保持不变。
回想一下我们之前的Component依赖Component。这个时候我们要让全局单例的Component在Application里进行Build,然后提供一个方法,提供给需要使用的子页面使用。定义超人类SurperMan
public class SurperMan {
}
定义全局单例Module
@Module
public class ApplicationMoule {
@Singleton
@Provides
SurperMan provideSurperMan() {
return new SurperMan();
}
}
定义全局单例Component,还记得Component依赖Component的知识吗,这里要把SurperMan返回出去(void inject(Application application)你加上也可以)
@Singleton
@Component(modules = ApplicationMoule.class)
public interface ApplicationComponent {
SurperMan provideSurperMan();
}
在Application里把我们的全局单例的Component build好。然后提供一个方法,返回给子页面
public class MyApplication extends Application {
private ApplicationComponent applicationComponent;
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent.builder().build();
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
}
重点来了,小伙伴们。首先是KstudyActivity我们的要用我们的全局单例。我第一篇文章也说了,一个页面不能有多个Component去注册,否则报错。那么怎么办呢。还记得dependencies吗。
这个时候父Component也准备好了:ApplicationComponent
这个时候我们之前的子Component,局域单例Component:ActivityComponent。需要依赖我们的父Component
@Singleton
@Component(modules = ActivityModule.class,dependencies = ApplicationComponent.class)
public interface ActivityComponent {
void inject(KstudyActivity kstudyActivity);
}
我先告诉你,看上去一切正常,但我告诉你会报错:
This @Singleton component cannot depend on scoped components
这个时候我也是懵逼的,在查阅大量资料。说是局域单例和全局单例在一个页面时,不能一起用@Singleton标注。这里我还是不理解为啥,有明白的小伙伴,望告知。
那么我们自定义@Scope,其实和@Singleton一模一样,只不过用了我们的类名
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
然后把我们的局域单例的子Component: ActivityComponent 和 子Module:ActivityModule 的@Singleton标注,改成我们的自定义Scope,@ApplicationScope。
然后看Activity里的代码,成果啊
public class KstudyActivity extends BaseActivity {
//作用域只在Activity里的单例
@Inject
Children children_1;
@Inject
Children children_2;
//作用域在全局的单例
@Inject
SurperMan surperMan;
@Override
protected void processLogic() {
ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
DaggerActivityComponent.builder().applicationComponent(applicationComponent)
.build().inject(this);
//全局单例
LogUtils.i("看看hashCode值", "作用域在activity里的单例 children_1 hashCode ==> " + children_1.hashCode());
LogUtils.i("看看hashCode值", "作用域在activity里的单例 children_2 hashCode ==> " + children_2.hashCode());
LogUtils.i("看看hashCode值", "作用域在Application里的全局单例 surperMan hashCode ==> " + surperMan.hashCode() + "");
}
}
最后一步,我们进2次KstudyActivity页面,以红线分割,看打印,打印如下:
是不是明显了,Children是局域单例,每次进页面都会被实例化。SurperMan是全局单例,hashCode值一直不会变。这个时候如果你还不清楚,我之前说了看Component在哪build:
我们的SurperMan的ApplicationComponent,在Application里build的
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent.builder().build();
}
而我们的Children的ActivityComponent,在哪build呢
protected void processLogic() {
ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
DaggerActivityComponent.builder().applicationComponent(applicationComponent)
.build().inject(this);
}
明白了吗?不明白再简化,是不是在Activity里build的
protected void processLogic() {
ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
ActivityComponent activityComponent = DaggerActivityComponent.builder().applicationComponent(applicationComponent)
.build();
activityComponent.inject(this)
}
4、Provider和Lazy的介绍和用法以及和@Inject的区别
Dagger2在Java中的使用,在我总结里,是最后一个知识点了。
首先我们看下定义3个类,我这里区分便于理解:猫,猪,羊。里面都一样,还是贴下代码。更清晰,都跟猫这样:
public class Cat {
}
然后是Module
@Module
public class AnimModule {
@Provides
Cat catProvides() {
return new Cat();
}
@Provides
Pig pigProvides() {
return new Pig();
}
@Provides
Sheep sheepProvides() {
return new Sheep();
}
}
然后是Component
@Component(modules = AnimModule.class)
public interface AnimComponent {
void inject(LstudyActivity lstudyActivity);
}
在Activity里我们打印他们的haschCode值
public class LstudyActivity extends BaseActivity {
//使用@Inject正常的。
@Inject
Cat cat_1;
@Inject
Cat cat_2;
//使用Provider
@Inject
Provider<Pig> pig_1;
@Inject
Provider<Pig> pig_2;
//使用Lazy
@Inject
Lazy<Sheep> sheep_1;
@Inject
Lazy<Sheep> sheep_2;
@Override
protected void processLogic() {
DaggerAnimComponent.builder().build().inject(this);
LogUtils.i("看看hashCode值", "@Inject cat_1的hashCode ==> " + cat_1.hashCode());
LogUtils.i("看看hashCode值", "@Inject cat_2的hashCode ==> " + cat_2.hashCode());
LogUtils.i("看看hashCode值", "Provider pig_1的hashCode ==> " + pig_1.hashCode());
LogUtils.i("看看hashCode值", "Provider pig_2的hashCode ==> " + pig_2.hashCode());
LogUtils.i("看看hashCode值", "Lazy sheep_1的hashCode ==> " + sheep_1.hashCode());
LogUtils.i("看看hashCode值", "Lazy sheep_2的hashCode ==> " + sheep_2.hashCode());
}
}
我同样进2次页面,以红线分割,看结果
首先我们可以看到这里的进2次页面,所有的hashCode都发生了变化,就是红线上和红线下一一对比,都发生改变了。
先别急,这里还看不出区别,因为Provider和Lazy还没有调用get(),我们单独,点击打印。看看情况:
Activity里的代码
public class LstudyActivity extends BaseActivity {
@Inject
Provider<Pig> pig_spec;
@Inject
Lazy<Sheep> sheep_spec;
@OnClick(R.id.btn)
public void specOnClick() {
LogUtils.i("看看hashCode值", "Provider pig_spec不通过get的hashCode ==> " + pig_spec.hashCode());
LogUtils.i("看看hashCode值", "Provider pig_spec的hashCode ==> " + pig_spec.get().hashCode());
LogUtils.i("看看hashCode值", "Lazy sheep_spec的hashCode ==> " + sheep_spec.get().hashCode());
}
}
我们点击2次按钮,以红线分割2次点击结果。看结果
- 可以看到不调用get()方法的Provider的的HashCode值。一直不变
- 调用get()方法的Provider的HashCode值,一直在改变
- 调用get()方法的Lazy的HashCode值,一直不变。
我们回看上一张图。provider修饰的pig_1、pig_2。不调用get()的是一直不变的。Lazy修饰的sheep_1,sheep_2,不调用get()方法。确是一直在变得。到底怎么理解呢
Provider和Lazy和@Inject的总结:
1、@Inject:在依赖注入对象的时候,每次都是实例化的。
2、Lazy:官方介绍是懒加载,调用.get()。才生成实例,而且一旦实例生成,在调用。会一直使用已经生成的实例。你去看生成的代码, Lazy也是Provider的一种,只不过用了LstudyActivity_MembersInjector.injectSheep_1(instance, DoubleCheck.lazy(sheepProvidesProvider));
那为什么我们第一张图,不调用get()方法的Lazy修饰的sheep_1和sheep_2的HashCode值,确不一样呢。这个时候我们看下代码
@Inject
Lazy<Sheep> sheep_1; //这里不通过get()方法,打印的不是Sheep实例,而是Lazy<Sheep>容器的hashCode
@Inject
Lazy<Sheep> sheep_2;
是不是明白了。调用get(),方法才会生成Sheep实例。这里的标注只是Lazy<T>,所以说这里定义了2个容器,所以才出现不调用get()方法的容器hashCode值才会不一样,调用了get()方法才会一样
3、Provider:其实是一种工厂模式,每次调用.get()。都会生成不同的实例;不调用不会生成实例。那么为什么不调用get()方法,hashCode反而一样呢
//这里一样,不调用get()方法是容器Provider<T>,说是容器,这里更可以说成是工厂
//因为每次调用get()方法,都实例化Pig的实例。所以这里只需要一个工厂(我是这么理解的)
@Inject
Provider<Pig> pig_1;
结束语:在Java里使用Dagger2还是很麻烦的。下片介绍在Android中使用,你会瞬间爱上Dagger2
本文github Demo地址
花了老大劲,别误会,不是要钱。能不能留下个足迹star啊