Android中,相比较RxJava、Retrofit等框架,Daager2可以说是最难上手的框架了,没有之一。Dagger2,一款依赖注入框架。看下官方对它的描述:
用于Android和java的一款高效的依赖注入工具。Dagger1.x由square开发,Dagger2由google公司接手,这里是Dagger2。我们先从一段代码讲起:
public class A {
private String aMsg;
public A(String aMsg) {
this.aMsg = aMsg;
}
}
public class Test {
public void test() {
A a = new A("msgA");
}
}
一段最简单不过的代码,Test类中的test方法中new了一个A类的对象,Test类依赖A类。我们称Test是依赖方,A是被依赖方。看起来似乎很完美。那么如果这样呢?
public class A {
private String aMsg;
public A(String aMsg) {
this.aMsg = aMsg;
}
}
public class Test {
public void test() {
A a = new A("msgA");
}
}
public class Test1 {
public void test() {
A a = new A("msgA");
}
}
Test、Test1两个类都依赖于A。有的人会说,依赖就依赖呗,我代码照样写,项目照样跑啊。是的,没错,你只要遵循java的语法,你的代码不会有任何问题。但是,如果有一天你的A类的构造方法新增了一个参数呢?你可能会说,改一下呗,实例化的时候多传一个参数就行了,的确可以,这里只有两个类依赖了A,改起来也很轻松,但假如有有100个类都依赖了A呢?我们这种做法就不是很优雅、很软工了,真要你改,你宁愿重新写一个类也不愿去改100个地方吧。其实回过头来我们发现:问题的症结在于A类的实例化放在了依赖方,导致A每次变动,都会导致Test类的变动,这就是传说中的耦合了。那么有没有办法解决这个问题呢?有的,就是我们的依赖注入(Dependency Injection)。依赖注入有三种方式:
1. 构造方法注入
public class Test {
private A a;
public Test(A a) {
this.a = a;
}
public void test() {
System.out.println(a.hashCode());
}
}
A类的实例化不放在Test类中,由构造方法传进来,这时无论A怎么变,都不需要再修改Test中的代码,Test只用关注自身的变化就好,不用再关注依赖A的变化。
2. setter方法注入
public class Test {
private A a;
public void test() {
System.out.println(a.hashCode());
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
在调用test方法之前确保调用了setA方法且传入的A对象不为空就好,能实现同样的效果。
3. 接口注入
public interface SetA {
void set(A a);
}
public class Test implements SetA {
private A a;
public void test() {
System.out.println(a.hashCode());
}
@Override
public void set(A a) {
this.a = a;
}
}
A类实现了一个SetA接口,同样在调用test之前能确保调用set(A a)方法且传入的A对象不为空即可。上述三种方式都是在解决一个问题:把A类的实例化从依赖方剥离,让依赖方不用关注依赖的变化,只关注自身的变化就好。
然而,依赖方的确不需要实例化依赖了,但是依赖对象不是从石头里蹦出来的啊,总要有地方去进行依赖的实例化。
而实现依赖注入的框架Dagger2就是我们今天的主角。本人是一位Android开发者,所以博客中关于Dagger2的demo都是基于Android平台下的。走你,先看下gradle中的依赖代码,如下:
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
implementation 'org.glassfish:javax.annotation:10.0-b28'
下面有下面一个需求:
有一个类Person,我需要在MainActivity中不调用Person的构造函数就能够使用。我们先来分析一下,这里MainActivity是依赖方,或者说需求方,即它需要一个Person类的实例。Person是被需求方。所谓有需求才有供给嘛,没有需求哪来的供给呢?在此之前我们先看两个注解:
@Inject和@Component
如果对注解还不太了解的话,请移步:注解的使用
@Inject注解是一个标签,它是给Dagger2看的,主要用在两个地方:
- 给一个类的构造方法注解。表示它可以提供一个依赖。
- 给一个类的属性做注解,表示这个属性是需求方,需要一个依赖对象。
- 给一个类的方法做注解,可以理解为属性的set方式,也是注入的目标。
这个注解就是用来标注需求方和被需求方。不过,仅仅凭这一个注解是不能让Dagger2正常运行的,我们还需要一个@Component注解。 @Component 相当于联系纽带,将 @inject 标记的需求方和依赖绑定起来,并建立了联系,而 Dagger2 在编译代码时会依靠这种关系来进行对应的依赖注入。好了,理论说完了,咋用呢?以上面需求为例,直接上代码:
public class Person {
@Inject
public Person() {
}
}
可以看到,我们在Person的构造方法上用 @Inject进行了注解。表示它是一个依赖的提供方,负责提供Person类的实例。
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
我们用 @Inject对MainActivity中的person变量进行了注解,表示它是一个依赖需求方,它需要一个Person类的实例。需求方和提供方都有了,还需要一个中间联系人MainComponent :
@Component
public interface MainComponent {
void inject(MainActivity mainActivity);
}
MainComponent 是一个接口,Dagger2会在编译的时候生成其对应的实现类,用来将提供方提供的实例注入到需求方。用 @Component 注解表示它是一个注入器,专门用来注入依赖实例的。void inject(MainActivity mainActivity)表示将依赖实例注入到哪里,这里我们就是将实例注入到MainActivity里。如果其他Activity也用到这个实例,重写一个方法就好了,把参数改成其它Activity就行。到此还没有完成,还缺少最终一步。Dagger2中采用了反射机制,我们知道,反射是比较消耗性能的,而Android对于性能这块看的比较重。Dagger2权衡之下,采用了APT(Annotation Process Tool) 在编译时生成辅助类,将比较耗费性能的反射放在了编译阶段。所以在使用之前我们还需要build一下项目,生成对应的辅助类(不build不会生成辅助类!!)。
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG, person.hashCode() + "");
}
}
DaggerMainComponent这个类不是我们建立的,是Dagger2在编译时自动生成的MainComponent的实现类。调用DaggerMainComponent.create().inject(this);这行代码之后,Person类的实例就被注入到MainActivity中了。我们就可以使用person属性了。看下打印的结果:
好吧,让我们来梳理下流程。
- MainAcivity需要一个Person类的实例,所以用@Inject注解标记在person变量上。同时Person类的构造方法也用@Inject注解标记了,表示它可以提供一个Person类的实例。
- 有需求也有供给,但是还需要一个中介,@Component注解标记的接口可以看做是一个中介。它负责把供给方提供的东西给到需求方。
这就是其中一种方式:@Inject构造方法+@Inject属性+Component。
刚刚说@Inject还可以注解在方法上(非构造方法)实现类似于setXX的效果。Person的代码无需改动,MainActivity代码稍作修改:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
private Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG, person.hashCode() + "");
}
@Inject
public void setPerson(Person person) {
this.person = person;
}
}
唯一不同的是,把属性上的@Inject挪到方法上了,实现类似于SetXX的效果,结果是一样的。
这样就完成了Dagger2的基本使用,Dagger2的其它用法都是基于此演变而来,万变不离其宗。
现在有这么一个场景,我在MainActivity里面需要一个ArrayList实例。有人说,在ArrayList的构造方法上和MainActivity上加注解,然后再弄个Component呗。然而,ArrayList的源码你能修改么?那可是java utils包下的源码,无法修改。凡是第三方的类,我们都不能修改,可是我又想用Dagger2,怎么搞?放心,你想到的问题Dagger2的开发者们都想到了,于是他们又推出了一个新概念:@Module 用于对@Inject做一个补充,解决我们无法在第三方类的构造方法中添加@Inject注解,而导致无法进行依赖注入的问题。
我们只需记住,@Module注解和@Inject注解所起到的作用是一致的,都是提供依赖的实例,只不过@Module注解更加全面,用法稍微不同而已。
那么怎么用呢?还是上面的问题,我们在MainActivity需要一个ArrayList实例,而我们不想去采用直接new的方式,就想采用Dagger2这种高逼格的方式进行依赖注入咋搞?
首先我们要定义一个类,一般名称叫做XXModule,这里因为和MainActivity关联,就起名MainModule,然后用@Module注解注解类名。表示这个类是用来提供依赖的,但依赖不是凭空生成的,需要在MainModule中定义一个提供依赖的方法,再用@Provides注解注解该方法表示这个方法是用来生成依赖实例的。
@Module
public class MainModule {
@Provides
ArrayList provideArrayList() {
return new ArrayList<>();
}
}
一般方法的起名都是provideXXX,这样能够顾名思义。其实Module就是一个普通的类,类中有一个提供依赖的方法,类名和方法分别用@Module和@Provides。这两个注解是给Dagger2看的。MainModule定义好了,我们的MainComponent还需要做稍微的修改:
@Component(modules = {MainModule.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
}
这里引用注射器的比喻,我们的MainActivity相当于一个病人,他需要一个药物ArrayList,而Component就相当于一个注射器,负责把药物注射到病人体内,而注射器本身是没有药物的,所以需要告诉注射器这个药物是来自于哪里的。@Component(modules = {MainModule.class})就是告诉Component告诉注射器病人需要的药物在某某地方,执行的时候,会先从病人的需求开始,病人需要哪种药物,我们通过@Component(modules = {MainModule.class})告诉了注射器病人需要的药物在MainModule类中,于是注射器会去寻找MainModule中是否存在一个返回值为ArrayList的方法,如果找到了,则会用该方法生成药物交给注射器,注射器再注射给病人,如果没有找到,则会报错,注射器就会说,你框我呢,我在你指定的地方根本没找到病人所需要的药物-。-
然后再看下MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
ArrayList arrayList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG,arrayList.hashCode()+"");
}
}
MainActivity的使用方法和之前几乎一样,DaggerMainComponent是我们build项目生成的MainComponent接口的实现类。看下结果:
说明我们的ArrayList对象已经成功注入到MainActivity中了。这里modules = {MainModule.class}是一个{},表明{}中可以放多个Module的class,表示可以提供不同的药物,举个例子?新建一个PersonModule:
@Module
public class PersonModule {
@Provides
Person providePerson() {
return new Person();
}
}
Component中的代码:
@Component(modules = {MainModule.class,PersonModule.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
}
MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
ArrayList arrayList;
@Inject
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG, arrayList.toString());
Log.e(TAG, person.hashCode() + "");
}
}
看下结果
可以看到ArrayList和Person实例都被注入到MainActivity中了。这应该很好理解。
上面我们说的都是类的注入,如果说我们MainActivity中需要的属性是一个接口呢,我们想注入一个接口实现类的实例,可不可以呢?新建一个People接口:
public interface People {
void work();
}
定义两个实现类:
public class Student implements People {
@Override
public void work() {
Log.e("TAG------","好好学习,天天向上~");
}
}
public class Teacher implements People {
@Override
public void work() {
Log.e("TAG------","我是一个园丁~");
}
}
Student和Teacher都实现了People接口,然后我们MainModule代码:
@Module
public class MainModule {
@Provides
Student provideStudent() {
return new Student();
}
@Provides
Teacher provideTeacher() {
return new Teacher();
}
}
MainActivity代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
People people;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
这时我们编译一下,会发现
我擦,报错了,为啥呢?其实也很简单,MainActivity需要一个People接口的实现类的对象,但是People接口的实现类可能不止一个,Component怎么知道你要哪个实现类的实例呢?但是,接口的引用指向子类的对象,这是一种很常用的用法,要如何实现呢?
引出@Named和@Qualifier
作用:
1.标记在provide方法上,用于区分来源。
2.标记在字段上,表示使用哪个来源。
先说@Named,MainModule代码如下:
@Module
public class MainModule {
@Provides
@Named ("student")
People provideStudent() {
return new Student();
}
@Provides
@Named ("teacher")
People provideTeacher() {
return new Teacher();
}
}
MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
@Named("student")
People student;
@Inject
@Named("teacher")
People teahcer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG, student.getClass().getName());
Log.e(TAG, teahcer.getClass().getName());
}
}
看下结果
可以看到两个接口的引用分别指向了实现类Student和Teacher。如何区分提供哪个实现类,就是@Named注解起的作用。我们来分析一下,MainActivity需要一个People接口的引用,我们不知道他需要哪个实现类的实例,这时他用 @Named 注解标记了一个名字,Component去Module寻找提供依赖的方法的时候,就会寻找被这个名字标记的方法,如果找到,则用这个方法提供实例,找不到的话就会报错。这就是 @Named 注解的作用,标记来源,用以区分。
@Qualifier
其实作用和@Named是一样的,只不过@Named注解是用字符串来标记的,而@Qualifier是用注解来标记的。首先定义两个注解:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface StudentQualifier {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface TeacherQualifier {
}
这两个注解StudentQualifier 和TeacherQualifier 都用@Qualifier标记了,我们怎么用它实现和@Named相同的功能呢?MainModule:
@Module
public class MainModule {
@Provides
@StudentQualifier
People provideStudent() {
return new Student();
}
@Provides
@TeacherQualifier
People provideTeacher() {
return new Teacher();
}
}
再看MainActivtiy。
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
@StudentQualifier
People student;
@Inject
@TeacherQualifier
People teahcer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG, student.getClass().getName());
Log.e(TAG, teahcer.getClass().getName());
}
}
是不是和@Named的原理一样,同时标记MainActivity和对应的provideXXX方法即可。结果依旧能同时注入,这里就不贴了。
@Scope
Scope的意思是域、范围的意思。被Scope标注的类,在一定的范围内是单例的,即只创建一次,多次使用都是同一个对象。举个例子?在MainActivity中需要注入两个Peron实例,但我希望两个Person实例用的同一个对象。这里首先看一下@Scope注解的源码:
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}
可以看到Scope的作用域是注解,即只能标注在注解上,也就是我们所说的元注解。如果对注解的概念还不太清楚,请移步注解详解 。既然只能标注在注解上,那我们自定义一个注解:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface MyScope {
}
自定义一个注解,并用@Scope注解标记。这个定义的注解是来帮助我们实现单例的需求的。那这个自定义的注解怎么去用呢?两种用法:
1. 给Component和provideXX方法同时加上该注解。
2. 给Component和Person类加上该注解。
无论如何,Component都需要添加该注解。:
@MyScope
@Component(modules = {MainModule.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
}
provideXX方法添加该注解:
@Module
public class MainModule {
@Provides
@MyScope
Person providePerson() {
return new Person();
}
}
MainActivity代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
Person person1;
@Inject
Person person2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG,person1.hashCode()+"");
Log.e(TAG,person2.hashCode()+"");
}
}
看下结果:
可以看到两个对象的哈希值是一样的,说明两个对象是同一个对象。那我们刚所说的在一定范围内是单例的是什么意思呢?现在我们在MainActivity中新增一个按钮,点击跳转到SecondActivity。代码就不贴了,只是加了一个点击事件, SecondActivity代码:
public class SecondActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
Person person1;
@Inject
Person person2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG+"SecondActivity", person1.hashCode() + "");
Log.e(TAG+"SecondActivity", person2.hashCode() + "");
}
}
和MainAcivity几乎相同,没啥好说的,唯一不同的是Log的时候加上了Activity的名称,便于区分Log。我们看一下MainActivity和SecondActivity所注入的单例是否是同一个对象。
可以看到,两个Activity都实现了所需依赖的单例,但这个单例不是全局的,换了一个Activity就会重新初始化依赖对象,这就是在一定范围的意思。
第二种方式,给Person类和Component添加注解:
@MyScope
public class Person {
@Inject
public Person() {
}
}
效果是相同的哈~
Lazy< T >
Lazy?懒加载?其实差不多就是这个意思。即使用的时候再去加载。我们之前@Inject T t并注入之后,Activity所需的依赖对象就已经创建了,我们拿到就可以直接用,但是@Inject Lazy< T > t并不会直接创建依赖对象,需要先get一下。看代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
Lazy<Person> person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
Log.e(TAG, person.get().hashCode() + "");
}
}
Lazy的get方法获取传入泛型的实例对象,第一次调用get会去创建Person对象,再次调用则会从缓存中取,不会去新建Person对象。多个Lazy之间不会共享缓存。
Provider< T >
作用和Lazy< T >相似,注入的时候不会立即创建依赖对象,等到调用get方法的时候再去创建依赖对象,和Lazy< T >不同的是,它不会缓存依赖对象,每次get都会去重新创建依赖对象。
@IntoSet
顾名思义,就是把提供的依赖对象加入到Set集合中。用法:
1.@IntoSet标注provideXX方法,将提供的依赖放入Set集合中。
2.需求方通过@Inject Set< T > set获取取得该Set集合。
@Module
public class MainModule {
//将提供的依赖放入Set集合中
@Provides
@IntoSet
Person providePerson() {
return new Person();
}
}
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
//获取Set<Person>对象。
@Inject
Set<Person> personSet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
for (Person person : personSet) {
Log.e(TAG, person.hashCode() + "");
}
}
}
结果:
这里说明三点:
- 相同返回值的provideXX方法会存入同一个Set集合中。
- 不同返回值的provideXX方法会存入不同的Set中。
- Component 注入多个目标类时,所有的目标类共享Set。
@IntoMap,顾名思义,是将提供的依赖存入Map中,以key和value的形式存入。用法和@IntoSet类似。key值用另一个标签 @StringKey,Module代码:
@Module
public class MainModule {
//将提供的依赖放入Map集合中
@Provides
@IntoMap
@StringKey("person1")
Person providePerson() {
return new Person();
}
//将提供的依赖放入Map集合中
@Provides
@IntoMap
@StringKey("person2")
Person providePerson2() {
return new Person();
}
}
Activity代码:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
//获取Map<String, Person>对象。
@Inject
Map<String, Person> map;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
for (Map.Entry<String, Person> entry : map.entrySet()) {
Log.e(entry.getKey(), entry.getValue().toString());
}
}
}
看下结果:
可以看到,我们提供了两个依赖对象,都存到同一个Map中了。不过@Stringkey只能标注String的key,标注自定义类型的Key下面这再说。
几点说明:
- 一个Component中存标注了多个Module,则所有的Module公用一个Map。
- Map中的值只能是具体的类型,不可以是接口类型。
- 两个Component之间的Map不共享,相互独立。
如何自定义Key的类型?
1.自定义一个注解,用@MapKey标记:
@MapKey
public @interface PersonKey {
Class value();
}
2.用@IntoMap和自定义的PersonKey注解标记provideXX方法:
@Module
public class MainModule {
//将提供的依赖放入Map集合中
@Provides
@IntoMap
@PersonKey(Person.class)
String provideStr1() {
return "person1";
}
}
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
Map<Person, String> map;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
for (Map.Entry<Person, String> entry : map.entrySet()) {
Log.e(entry.getKey().hashCode()+"", entry.getValue().toString());
}
}
}
结果:
可以看到,key是Person类的哈希值,value是我们提供的一串字符串。自定义key支持哪些类型呢?
1.基本数据类型
2.String
3.Class
4.枚举类型
5.注解类型
6.以上数据类型的数组
我们刚用的是Class类型。其它的各位看官感兴趣可以逐一试一试。常用的也就1、2、4,其它的用的比较少。
@Component的dependencies
我们之前说过,Component中可以标记多个Module,用法是@Component(modules = {MainModule.class}),里面可以放多个Module.class,这样Component就可以注射不同的依赖了。@Component还可以通过@Component(dependencies = {})的方式来提供不同的依赖,效果类似,不同的是{}中对应的是XXComponent.class。比如:
@Component(dependencies = {PersonComponent.class})
public interface MainComponent {
void inject(MainActivity mainActivity);
void inject(SecondActivity secondActivity);
}
@Subcomponent
看名字是子Component,实际上也是这个意思。Component也可以有类似java类的继承关系。比如有一个MainComponent,可以提供一串字符串依赖,有一个PersonComponent继承自MainComponent,它既有MainComponent的功能,自己本身又能提供一个Person依赖:
//注意这里是@Subcomponent
@Subcomponent(modules = {PersonModule.class})
public interface PersonComponent {
void inject(MainActivity mainActivity);
}
@Module
public class PersonModule {
@Provides
Person providePerson() {
return new Person();
}
}
@Component(modules = {MainModule.class})
public interface MainComponent {
//添加子Component。
PersonComponent subMainComponent();
void inject(MainActivity mainActivity);
void inject(SecondActivity secondActivity);
}
@Module
public class MainModule {
@Provides
String provideStr1() {
return "person1";
}
}
这里的写法不是PersonComponent extents MainComponent ,而是在MainComponent中进行标记。并用@Subcomponent标记PersonModule,表示这是一个子Component。看下Activity中的调用:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "TAG_MAIN";
@Inject
String mainModuleProvideStr;//String来自于MainComponent
@Inject
Person personModuleProvidePerson;//person来自于PersonComponent
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取到子Component,并将子Component中提供的依赖注入进来。
DaggerMainComponent.create().subMainComponent().inject(this);
Log.e(TAG,mainModuleProvideStr);
Log.e(TAG,personModuleProvidePerson.hashCode()+"");
}
}
但是一定要调用DaggerMainComponent.create().subMainComponent(),否则获取不到子Component。 看起来不像继承,更准确的说应该是依附关系。虽说是一种继承关系,但就这个写法来看,说依附可能更好理解一点。
总结:,Dagger2确实解决了程序中依赖耦合的问题,是一款非常好的依赖注入框架,用法也很丰富,可以解决各种需求下的依赖注入问题,但说实话,上手的成本有点高,是我目前用过的框架最难上手的一款,没有之一,而且出了错误也不是很直观,用与不用还需要做一个权衡。
文章就先写到这里,如果文章中有什么错误或者不严谨的地方,欢迎批评指正。
完~~~~