1. 添加依赖
1. 在Project的 build.gradle文件添加
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'me.tatarka:gradle-retrolambda:3.2.4'
2. 在App下的build.gradle添加
头部添加
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'me.tatarka.retrolambda'
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
apt 'com.google.dagger:dagger-compiler:2.2'
compile 'com.google.dagger:dagger:2.2'
provided 'org.glassfish:javax.annotation:10.0-b28'
}
3. 如果下载不下来可以在gradle.properties文件中设置代理 我这里是设置的shadowsocks的代理
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
2. 基础概念
依赖注入:就是目标类(目标类需要进行依赖初始化的类,下面都会用目标类一词来指代)中
所依赖的其他的类的初始化过程,不是通过手动编码的方式创建,而是通过技术手段可以把
其他的类的已经初始化好的实例自动注入到目标类中。
依赖注入的过程:
步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数
步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
Inject用来标注依赖和被依赖的构造函数
Provides提供依赖的方法上添加的注解,provide方法需要包含在Module中
Module专门提供依赖,类似工厂模式,包含Provides方法
Component它是一个桥梁,一端是目标类,另一端是目标类所依赖类的实例,它也是注入器(Injector)负责把目标类所依赖类的实例注入到目标类中,同时它也管理Module。(先从Module中找依赖,再从Inject找构造函数)
Module和Provides是为解决第三方类库而生的,Module是一个简单工厂模式,Module可以包含创建类实例的方法,这些方法用Provides来标注
Scope自定义注解,用于标示作用域,命名随意,对应即可,其中@Singleton是一个系统的模式实现.(管理Module与Component的匹配)
Qualifier自定义注解,用于解决一个实例可以被多种方式构建的依赖迷失问题
Component有三种组织关系,分为依赖,包含和继承,用于解决依赖复用与共享问题
依赖方式
一个Component是依赖于一个或多个Component,Component中的dependencies属性就是依赖方式的具体实现包含方式
一个Component是包含一个或多个Component的,被包含的Component还可以继续包含其他的Component。这种方式特别像Activity与Fragment的关系。SubComponent就是包含方式的具体实现。- 继承方式
官网没有提到该方式,具体没有提到的原因我觉得应该是,该方式不是解决类实例共享的问题,而是从更好的管理、维护Component的角度,把一些Component共有的方法抽象到一个父类中,然后子Component继承。
Scope用法
更好的管理Component之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定义的Scope注解标注这些Component,这些注解最好不要一样了,不一样是为了能更好的体现出Component之间的组织方式。还有编译器检查有依赖关系或包含关系的Component,若发现有Component没有用自定义Scope注解标注,则会报错。
更好的管理Component与Module之间的匹配关系,编译器会检查 Component管理的Modules,若发现标注Component的自定义Scope注解与Modules中的标注创建类实例方法的注解不一样,就会报错。
可读性提高,如用Singleton标注全局类,这样让程序猿立马就能明白这类是全局单例类。
3. @Inject 和 @Component使用
新建一个类
public class Person {
private String desc;
@Inject
public Person(){
this.desc = "生命不息奋斗不止";
}
public Person(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
MainActivity中使用这个类
public class MainActivity extends AppCompatActivity {
//添加@Inject注解,表示这个mPoetry是需要注入的
@Inject
Person person;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTextView = (TextView) findViewById(R.id.id_tv);
mTextView.setText(person.getDesc());
}
}
需要一个连接器Component,让上面这两个类产生联系
//用@Component表示这个接口是一个连接器,能用@Component注解的只能是interface或者抽象类
@Component
public interface MainComponent {
/**
* 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
* (被标记为@Inject的属性)
* 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
* 用inject即可。
*/
void inject(MainActivity activity);
}
修改MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用Dagger2生成的类 生成组件进行构造,并注入
DaggerMainComponent.builder().build().inject(this);
initView();
}
4. @Module 和 @Provides 和 @Component使用
Module和Provides是为解决第三方类库而生的,Module是一个简单工厂模式,Module可以包含创建类实例的方法,这些方法用Provides来标注
@Module
public class MainModule {
@Provides
public Gson provideGson(){
return new Gson();
}
}
添加完这个类后,我们要与之前写的类产生关联
修改MainCompontent
//这里表示Component会从MainModule类中拿那些用@Provides注解的方法来生成需要注入的实例
@Component(modules = MainModule.class)
public interface MainComponent {
/**
* 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
* (被标记为@Inject的属性)
* 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
* 用inject即可。
*/
void inject(MainActivity activity);
}
这里多了一个依赖,依赖MainModule类中的方法生成Gson实例,我们在MainActivity里注入Gson实例:
public class MainActivity extends AppCompatActivity {
//添加@Inject注解,表示这个person是需要注入的
@Inject
Person person;
@Inject
Gson mGson;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用Dagger2生成的类 生成组件进行构造,并注入
DaggerMainComponent.builder().build().inject(this);
initView();
}
private void initView() {
mTextView = (TextView) findViewById(R.id.id_tv);
String json = mGson.toJson(person);
mTextView.setText(json);
}
}
5. @Component依赖多个@Module
创建一个新的PeronModule
@Module
public class PeronModule {
// 这个方法需要一个String参数,在Dagger2注入中,这些参数也是注入形式的,也就是
// 要有其他方提供参数desc的生成,不然会造成编译出错
@Provides
public Person providePerson(String desc){
return new Person(desc);
}
// 这里提供了一个生成String的方法,在这个Module里生成Person实例时,会查找到这里
// 可以为上面提供String类型的参数
@Provides
public String getDesc(){
return "代码改变世界 双手成就未来";
}
}
修改MainComponent依赖:
//这里表示Component会从MainModule和PeronModule类中拿那些用@Provides注解的方法来生成需要注入的实例
@Component(modules = {MainModule.class,PeronModule.class})
public interface MainComponent {
/**
* 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
* (被标记为@Inject的属性)
* 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
* 用inject即可。
*/
void inject(MainActivity activity);
}
Dagger2是先从@Privodes查找类实例,如果找到了就用@Module提供的方法来创建类实例,
如果没有就从构造函数里用@Inject注解的生成类实例,如果二者都没有,则报错,
简而言之,就是@Module的优先级高于@Inject。
6. @Scope
我们创建多一个Activity,这个Activity也注入了Person和Gson对象:
public class SecondActivity extends AppCompatActivity {
@Inject
Person p;
@Inject
Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent.getInstance().inject(this);
TextView id_tv = (TextView) findViewById(R.id.id_tv);
String json = gson.toJson(p);
id_tv.setText(json + " \nperson="+p);
}
}
把MainComponent改成抽象类的形式,并添加返回MainComponent单例的方法
@Component(modules = {MainModule.class,PoetryModule.class})
public abstract class MainComponent {
/**
* 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
* (被标记为@Inject的属性)
* 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
* 用inject即可。
*/
abstract void inject(MainActivity activity);
abstract void inject(SecondActivity activity);
private static final class SingleHolder{
private final static MainComponent mainComponent = DaggerMainComponent.builder().build();
}
public static MainComponent getInstance(){
return SingleHolder.mainComponent;
}
}
调用同一个MainComponent实例多次注入的时候还是会重新生成Person实例,
有时候我们需要只希望生成一个共用实例的时候应该怎么办呢,这里我们就需要用到
Dagger2的@Scope属性了,Scope是作用域的意思,我们先自定义一个@Scope注解:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonScore {
}
同时在Module与Component加上这个自定义Scope
@PersonScore
@Component(modules = {MainModule.class,PeronModule.class})
public abstract class MainComponent {
/**
* 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
* (被标记为@Inject的属性)
* 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
* 用inject即可。
*/
abstract void inject(MainActivity activity);
abstract void inject(OtherActivity activity);
private static final class SingleHolder{
private final static MainComponent mainComponent = DaggerMainComponent.builder().build();
}
public static MainComponent getInstance(){
return SingleHolder.mainComponent;
}
}
@Module
public class PeronModule {
@PersonScore
@Provides
public Person providePerson(String desc){
return new Person(desc);
}
@Provides
public String getDesc(){
return "代码改变世界 双手成就未来";
}
}
通过实现自定义@Scope注解,标记当前生成对象的使用范围,标识一个类型的注射器只实例化一次,
在同一个作用域内,只会生成一个实例,然后在此作用域内共用一个实例,
@Singleton其实就是@Scope的一个默认实现而已
7. 组织Component
1. 依赖方式
一个Component可以依赖一个或多个Component,并拿到被依赖Component暴露出来的实例,
Component的dependencies属性就是确定依赖关系的实现。如我们在ApplicationModule提供了
一个全局的Gson对象,我们想要提供给其他Component时,要在ApplicationComponent显式的提供一个接口:
创建一个新的Module
@Module
public class ApplicationModule {
@Singleton
@Provides
public Gson provideGson(){
return new Gson();
}
}
//创建新的Component
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Gson getGson(); //获取Gson对象
}
在自定义的MyApplication中初始化它,更改MainComponent:
public class MyApplication extends Application {
private static MyApplication application;
private static ApplicationComponent applicationComponent;
@Override
public void onCreate() {
super.onCreate();
application = this;
applicationComponent = DaggerApplicationComponent.create();
}
public static MyApplication getApplication() {
return application;
}
public static ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
}
//这里表示Component会从MainModule类中拿那些用@Provides注解的方法来生成需要注入的实例
@PersonScore
@Component(dependencies = ApplicationComponent.class, modules = {MainModule.class,PersonModule.class})
public abstract class MainComponent {
/**
* 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
* (被标记为@Inject的属性)
* 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
* 用inject即可。
*/
public abstract void inject(MainActivity activity);
public abstract void inject(OtherActivity activity);
private static final class SingleHolder{
private final static MainComponent mainComponent = DaggerMainComponent.builder()
.applicationComponent(MyApplication.getApplicationComponent()).build();
}
public static MainComponent getInstance(){
return SingleHolder.mainComponent;
}
}
这样就达到了MainComponent依赖ApplicationComponent。并且这里需要注意的是,
MainComponent的作用域不能和ApplicationComponent的作用域一样,否则会报错,
一般来讲,我们应该对每个Component都定义不同的作用域。
2. 包含方式(从属方式)@SubComponent
如果我们需要父组件全部的提供对象,这时我们可以用包含方式而不是用依赖方式,
相比于依赖方式,包含方式不需要父组件显式显露对象,就可以拿到父组件全部对象。
SubComponent只需要在父Component接口中声明就可以了
定义一个新的Scope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface SubScope {
}
定义一个新的Module
@Module
public class SubModule {
@SubScope
@Provides
public Person getPerson(){
return new Person("生活不易,且行且珍惜吧");
}
}
定义一个新的Component
@SubScope
@Subcomponent(modules = SubModule.class)
public interface SubComponent {
void inject(ThreeActivity activity);
}
在ApplicationComponent中添加一个创建SubComponent的方法
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Gson getGson();
SubComponent createComponent(SubModule module);
}
自定义的MyApplication中添加一个方法
private SubComponent subComponent;
public SubComponent getSubComponent(){
if (subComponent == null){
subComponent = applicationComponent.createComponent(new SubModule());
}
return subComponent;
}
新创捷一个ThreeActivity
public class ThreeActivity extends AppCompatActivity {
@Inject
Person p;
@Inject
Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApplication.getApplication().getSubComponent().inject(this);
findViewById(R.id.id_btn).setVisibility(View.INVISIBLE);
TextView id_tv = (TextView) findViewById(R.id.id_tv);
String text = p.getDesc()+" \n,person:"+p+"\n"+( gson == null ? "Gson没被注入" : "Gson已经被注入");
id_tv.setText(text);
}
}
8. @Qualifier
假如在上面的ThreeAtivity里面我们想要注入两个不同的Person(指desc不一样)实例,
我们可以在SubModule下添加多一个生成Person的方法:
自定义@Qualifier限定符
@Qualifier
@Documented
public @interface PersonQualifier {
String value() default "";
}
@Module
public class SubModule {
@PersonQualifier("a")
@SubScope
@Provides
public Person getPerson(){
return new Person("生活不易,且行且珍惜吧");
}
@PersonQualifier("b")
@SubScope
@Provides
public Person getPerson2(){
return new Person("呵呵呵呵呵呵呵");
}
}
修改Activity代码
public class ThreeActivity extends AppCompatActivity {
@PersonQualifier("a")
@Inject
Person p;
@PersonQualifier("b")
@Inject
Person p1;
@Inject
Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApplication.getApplication().getSubComponent().inject(this);
findViewById(R.id.id_btn).setVisibility(View.INVISIBLE);
TextView id_tv = (TextView) findViewById(R.id.id_tv);
String text = p.getDesc()+" \n,person:"+p+"\n"+p1.getDesc()+" \n,person:"+p1+"\n"+( gson == null ? "Gson没被注入" : "Gson已经被注入");
id_tv.setText(text);
}
}
Dagger2已经默认帮我们实现了一个@Named和自定义的PersonQualifier其实是一样的。
9.联系方式
qq:1509815887@qq.com
email : zlc921022@163.com
phone : 18684732678
10. 参考
Dagger2 使用详解
Android:dagger2让你爱不释手-基础依赖注入框架篇
Android:dagger2让你爱不释手-重点概念讲解、融合篇
Android:dagger2让你爱不释手-终结篇