文章目录
0.前言
接上一篇入门文章,这篇主要是各属性实战。
1.Dagger2各属性了解
- 必要属性
@inject//注入,@Component,@Moudle,@Provider
为什么说这个几个是必要属性,因为只要想用dagger2这几个属性是绕不开的。 - 高级属性
@Named @Qualifier @Scope @Singleton;
这四个属性实际上可以分文俩组,@Named底层实现是@Qualifier,@Singleton底层实现是@Scope
2.Dagger2各属性使用及分析
2.1.@Inject
注解构造函数:
通过标记构造函数,告诉Dagger 2可以创建该类的实例(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖关系。
若注解了构造函数,没有 注解依赖变量,将不创建实例。
注解依赖变量:
通过标记依赖变量,Dagger2提供依赖关系,注入变量。
若注解了依赖变量,而没注解构造函数,将获得一个null对象。
2.2.@Moudle
通过注解类,为Dagger提供依赖关系。
@Module
public class TestModule {
@Provides
public Test provideTest() {
return new Test();
}
}
2.3.@Provider
@Provides依然是为了提供依赖关系,是对@Inject的补充,弥补了@inject对于第三方框架和接口类的盲区。
@Provides方法本身可能也拥有自身的依赖关系。
@Provides方法本身是不能独立存在的,它必须依附于一个Module。
2.4.@Component
通过注解接口类,作为提供依赖和所需依赖之间的桥梁(链接Moudle 和 Containter),在编译时生成相关实例,将相关依赖注入完成使命。
@Component(modules={Test.class,Test1.class})
public interface TestComponent{
void inject(Class class);
}
依赖规则
若@Injdec和@Module都提供了依赖对象
1.查找Module中是否存在创建该类的方法
2.若存在,初始化参数,完成实例创建
3.不存在用@Inject创建实例
2.5.@Singleton
这个注解的意思就是单例。在Dagger2的使用中,需要有局部单例和全局单例的意识,那么什么是局部单例,可以理解为在一个Activity内n个类型相同的变量地址相同,但超出了这个Activity到了另一个类里边定义这类型的变量地址便不同了,就像是人这个物种,在屋子1里边全是男人,但是在屋子2里边就全是女人了;全局单例就好理解了,同一类型的变量在整个app内的任何地方,地址都是一样的。
不管是局部或者全局的单例,他们其实基本定义都一样,唯一的差别就是Component的作用域,若Component的作用域是全局的,那么对应@Singleton或者@Scope的对应moudle就是全局的,反之亦然,这么说有点抽象,看如下代码:
package com.zjandroid.nani.constant;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class UrlConstant {
@Singleton
@Provides
public UrlConstant instance(){
return new UrlConstant();
}
}
这是项目内网络地址管理的字符串常量类,我把它作为一个Moudle,并在它的实例化方法上使用了 @Singleton注解,接着我又定义了2个Component(作用域为局部):
package com.zjandroid.nani.dagger.component;
import com.zjandroid.nani.constant.UrlConstant;
import com.zjandroid.nani.view.activity.SplashActivity;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component (modules={UrlConstant.class})
public interface SplashComponent {
void inject(SplashActivity splashActivity);
}
package com.zjandroid.nani.dagger.component;
import com.zjandroid.nani.constant.UrlConstant;
import com.zjandroid.nani.view.activity.MainActivity;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component (modules={UrlConstant.class})
public interface MainComponent {
void inject(MainActivity splashActivity);
}
这俩个Component分别对应SplashActivity和Mainactivity,接下来就是去这俩个Acitivity内进行注入并观察输出结果,这俩个Acitivty内啥逻辑都没有,唯一有意义的就是这一行代码:
XXXComponent.builder().urlConstant(new UrlConstant()).build().inject(this);
public class MainActivity extends BaseActivity {
@Inject
UrlConstant mUrlConstant11;
@Inject
UrlConstant mUrlConstant22;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder().urlConstant(new UrlConstant()).build().inject(this);
LogUtils.d("mUrlConstant11 == "+ mUrlConstant11.toString());
LogUtils.d("mUrlConstant22 == "+ mUrlConstant22.toString());
}
}
public class SplashActivity extends BaseActivity {
@Inject
UrlConstant urlConstant1;
@Inject
UrlConstant urlConstant2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
DaggerSplashComponent.builder().urlConstant(new UrlConstant()).build().inject(this);
LogUtils.d("mUrlConstant1 == "+ urlConstant1.toString());
LogUtils.d("mUrlConstant2 == "+ urlConstant2.toString());
startActivity(new Intent(SplashActivity.this, MainActivity.class));
}
}
局部单例的结果:不同Acitivty的地址不一样
║ mUrlConstant1 == com.zjandroid.nani.constant.UrlConstant@4a7f5dac
║ mUrlConstant2 == com.zjandroid.nani.constant.UrlConstant@4a7f5dac
║ mUrlConstant11 == com.zjandroid.nani.constant.UrlConstant@4a8063c0
║ mUrlConstant22 == com.zjandroid.nani.constant.UrlConstant@4a8063c0
接下来实现全局的单例:
全局单例的核心就一点,用同一个component ,并声明同一个Singleton(Scope) ,创建出来的是同一个对象。
当Component对象是全局唯一,它所对应的@Singleton~Moudle就是一个全局单例,它inject出来的对象也是单例。具体到代码上,就是造一个全局唯一的Component让子Activity拿着自己inject自己。
接下来是具体实施步骤:
1.新增一个GlobalComponent ;
@Singleton
@Component(modules={UrlConstant.class})
public interface GlobalComponent {
void inject(BaseActivity baseActivity );
}
2.在Application中实例化它;
pps:不是说非得在Application实例化,只是利用了它全局唯一特性,完全可以自己另启一个全局唯一的类来承载,但没必要;
public class NaNiApplication extends Application {
private GlobalComponent mGlobalComponent;
/**
* 获取全局单例Global
* @return
*/
public GlobalComponent getGlobalComponent() {
if(mGlobalComponent == null)
mGlobalComponent = DaggerGlobalComponent.builder().build();
return mGlobalComponent;
}
}
3.Client中调用
对于Client的选择,我倾向于选择父类中进行初始化,然后加标记为进行控制,对子类暴露,这样,子类想要的时候可以直接拿,更重要的,可以避免Componet中的inject(XXXActivity)新增类的问题;
public abstract class BaseActivity extends NetObserverActivity {
@Inject
UrlConstant mUrlConstant;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!notInitGlobalComponent())
((NaNiApplication) getApplication()).getGlobalComponent().inject(this);
}
protected abstract boolean notInitGlobalComponent();
public UrlConstant getUrlConstant() {
return mUrlConstant;
}
}
public class SplashActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
LogUtils.d("mUrlConstant11 == "+ getUrlConstant().toString());
startActivity(new Intent(SplashActivity.this, MainActivity.class));
}
@Override
protected boolean notInitGlobalComponent() {
return false;
}
}
public class MainActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogUtils.d("mUrlConstant11 == "+ getUrlConstant().toString());
}
@Override
protected boolean notInitGlobalComponent() {
return false;
}
}
输出结果
║ mUrlConstant11 == com.zjandroid.nani.constant.UrlConstant@4a7ea1e0
║ mUrlConstant11 == com.zjandroid.nani.constant.UrlConstant@4a7ea1e0
2.6.@Scope
0>Scope是Singleton的底层实现,一般在工程实施中,它作为提高可读性的手段之一让程序员可以一眼就看出哪些是全局哪些是局部的生命周期
1>来标识范围的注解,并控制如何重复使用类的实例。仅用@inject注解,未用@Scope注解,在每次依赖注入的时候都会创建新的实例。使用@Scope注解会缓存第一次创建的实例,然后重复注入缓存的实例,不会创建新的实例。
2>如果有类注入实例的类被@Scope注解,那么其Component必须被相同的Scope注解。
3>如果依赖实例的注入来源是@Provides方法时,@Provides方法必须被@Scope注解;
4>如果依赖实例的注入来源是@Inject注解的构造函数时,实例类必须被@Scope注解。@Scope实际上是对注入器的控制。
另外在看其它博客文章的时候,你会经常看到 @ActivityScope 可以声明一个Activity生命周期的对象 ,@ApplicationScope 可以声明一个Application生命周期的对象 , 难道这些Scope这么神? 定义一个名字就可以控制对象的生命周期了? 其实这和 @Singleton一样的,都是代码是否通过同一个 component 对象来控制的。比如 @ActivityScope 定义的对象 ,其在Activity创建了component对象 ,那这个component对象肯定在这个Activity的生命周期内啊,依赖创建出来的对象也肯定是这个Activity啊。还有@ApplicationScope 中的component 对象是在 Application中的,那依赖创建出来的对象的生命周期也肯定是和 @Singleton的一样的,单例的生命周期不就是整个 Application 吗。
2.7.@Qualifier
这是限定符,它可以标记不同的构造方法让外部调用者获取期望的构造对象,比如:
比如Person类,他有一个属性Sex,new了第一个Person,设置sex=“male”,new了第二个Person,设置sex=“female”,我们如何在注入依赖时候,准确注入一个男人或者女人呢?
/**
* 自定义一个限定符
*/
@Qualifier//限定符
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Type {
String value() default "";//默认值为""
}
首先自定义一个注解@interface,刨开2个元注解不看 @Documented @Retention,顶部定义一个 @Qualifier限定符标识,然后这一句String value() default ""
的意思是,Type注解可以传参,默认为一个空字符串。
定义好了这个注解就相当于定义好了一个可接收参数的标签,接下来我们要将这个标签传入不同的参数然后分别贴到不同的构造函数之上(记住其中的关系,@Provide + @Type("")区分不同构造函数,Type又被@Qualifier修饰),这样,就相当于让每个构造函数有了辨识的途径,若要使用不同的构造函数,则只需去揭开不同的标签即可。
/**
* 定义Module
*/
@Module public class SalaModule {
.........
@Type("normal")//使用限定符来区别使用哪个构造函数返回对象
@Provides public Apple provideNormalApple() {
return new Apple();
}
@Type("color")
//使用限定符来区别使用哪个构造函数返回对象
@Provides public Apple provideColorApple(String color) {
return new Apple(color);
}
//由于我们的Apple构造函数里使用了String,所以这里要管理这个String(★否则报错)
//int等基本数据类型是不需要这样做的
@Provides public String provideString(){
return new String();
}
}
Module传入不同参数分别标记不同的构造方法。
/**
* 编写Component
*/
@Component(modules = {SaladModule.class})
//指明要在那些Module里寻找依赖
public interface SaladComponent {
.........
@Type("normal")
//关键靠这个限定符来区分调用哪个构造函数
Apple provideNormalApple();
//这个方法名并不重要,只要是provide开头就行,但是建议和module里一致
@Type("color")
Apple provideColorApple();
String provideString();
//注意:下面的这个方法,表示要将以上的三个依赖注入到某个类中
// 这里我们把上面的三个依赖注入到Salad中
// 因为我们要做沙拉
void inject(Salad salad);
}
Component通过刚才自定义的@Type注解标注对外暴露的接口,当这个接口方法被调用时,它会去Moudle内找对应的相同的@Type参数列表一致的构造与provider方法。
2.8.@Named
Named底层实现是@Qualifier。