Dagger2
做为一个上手难度较高的框架,我也是看了许多相关的文章,经历了无数次的从入门到放弃。放弃的多了好像也有一点懂了,因而乎我也总结一下本身对Dagger2
使用的相关知识的理解。git
关于Dagger2
首先要理解的就是依赖注入(DI)和控制反转(IOC),对这两个概念你若是已经有所了解,能够直接跳到下一节。github
在理解依赖注入以前先了解依赖注入的目的,也是使用Dagger2
框架的目的,知道了目的才能更好地理解过程。依赖注入的目的就是二个字:解耦。编程
高内聚,低耦合,是面向对象编程提倡遵循的设计原则,而经过依赖注入的方式能实现控制反转,从而实现解耦的目的。bash
光这样说仍是太理论了,不易理解,举个例子来帮助理解下,最近复联4大火,举个漫威英雄的例子。markdown
首先想象一下,你是个自带柯南属性普通人,每次有外星人入侵或者是超能力变种人搞破坏,你都好巧不巧的能出如今现场,然而你并无能力战胜他们,你只有托尼史塔克的联系方式,因此你每次都联系他,由他来解决这些麻烦。然而托尼忽然有一天告诉你他要带着小辣椒去度假,会有一段时间联系不上。你没办法出于求生本能,你得去寻找另外一个超级英雄,你找到了美国队长,队长一口答应,说没问题,下次有事联系我,我来搞定。因而你得到了美国队长的联系方式。又过一段时间,队长也和你说他要和冬兵去度假,然而托尼还度假没回来。你没办法,又只能本身去找寡姐,得到了他的联系方式,下次遇到袭击就联系她。发现了没有,这里你每次都依赖与某一个超级英雄,一旦发生变故,你只能本身去找新的英雄得到更新他的联系方式。这样对某个英雄依赖很是严重,如今换一种方法,不具体依赖某个英雄,我直接去神盾局找尼克弗瑞获得他的联系方式,之后有事都联系他,至因而哪一个超级英雄来解决又或者怎么去联系英雄,我都不用知道,交给尼克弗瑞去处理就行。网络
下面经过代码加深理解,先定义三个具体英雄类:架构
//抽象英雄类
public abstract class Hero {
public abstract String call();
}
复制代码
public class IronMan extends Hero { @Override public String call() { return "贾维斯:已收到您的求救消息,正在联系托尼"; } } 复制代码
public class CaptainAmerica extends Hero { @Override public String call() { return "美国队长:我已收到您的求救消息,正在赶来的路上"; } } 复制代码
public class BlackWidow extends Hero { @Override public String call() { return "黑寡妇:我已收到您的求救消息,正在赶来的路上"; } } 复制代码
最后是本身类:app
public class Self { private IronMan ironMan; public Self() { ironMan = new IronMan(); } public void help() { String call = ironMan.call(); Log.d("callMessage", call); } } 复制代码
调用:框架
//首先要有一个我
Self self = new Self();
//遇到危险
self.help();
复制代码
执行日志:ide
D/callMessage: 贾维斯:已收到您的求救消息,正在联系托尼
复制代码
托尼去度假了,因而咱们只能联系美队,因此要修改Self
类:
public class Self { // private IronMan ironMan; private CaptainAmerica captainAmerica; public Self() { // ironMan = new IronMan(); captainAmerica = new CaptainAmerica(); } public void help() { // String call = ironMan.call(); String call = captainAmerica.call(); Log.d("callMessage", call); } } 复制代码
执行日志:
D/callMessage: 美国队长:我已收到您的求救消息,正在赶来的路上
复制代码
美队也去度假了,再次修改Self
类:
public class Self { // private IronMan ironMan; // private CaptainAmerica captainAmerica; private BlackWidow blackWidowa; public Self() { // ironMan = new IronMan(); // captainAmerica = new CaptainAmerica(); blackWidowa = new BlackWidow(); } public void help() { // String call = ironMan.call(); // String call = captainAmerica.call(); String call = blackWidowa.call(); Log.d("callMessage", call); } } 复制代码
执行日志:
D/callMessage: 黑寡妇:我已收到您的求救消息,正在赶来的路上
复制代码
看到这里发现每次变更都要修改Self
类,这里Self
一直依赖一个英雄类,英雄更换了要修改,英雄的构造函数变了也要修改Self
类,这样耦合就很是严重。如今咱们采用经过尼克弗瑞来联系英雄:
public class NickFury { private Hero hero; public Hero call() { hero = new CaptainAmerica(); return hero; } } 复制代码
修改Self
类:
public class Self { private NickFury nickFury; public Self() { nickFury = new NickFury(); } public void help() { Hero hero = nickFury.call(); String call = hero.call(); Log.d("callMessage", call); } } 复制代码
执行日志:
D/callMessage:美国队长:我已收到您的求救消息,正在赶来的路上
复制代码
这下Self
类不依赖于具体某个英雄类,而是经过三方NickFury
类来实现英雄对象的注入。一旦有所变更更换英雄,只须要修改NickFury
类的方法便可。其实这里的NickFury类,相似于工厂模式。
还记的依赖注入的目的吗?解耦,这里经过第三方工厂类使具体英雄类与Self
类再也不耦合,原来是Self
主动去new
实例化一个英雄类,修改后变为被动经过调用第三方类方法注入一个英雄类,由主动到被动实现了控制反转,实现了解耦,达成了这个目的。
关于依赖注入的方法有如下几种:
基于接口:
public interface InjectInterface {
void injectHero(Hero hero);
}
复制代码
public class MySelf implements InjectInterface { Hero hero; public void help() { String call = hero.call(); Log.d("callMessage", call); } @Override public void injectHero(Hero hero) { this.hero = hero; } } 复制代码
定义一个接口,实现类实现接口方法注入。
基于构造函数:
public class MySelf { private Hero hero; public MySelf(Hero hero) { this.hero = hero; } public void help() { String call = hero.call(); Log.d("callMessage", call); } } 复制代码
在构造函数时传入。
基于set方法:
public class MySelf { private Hero hero; public void setHero(Hero hero) { this.hero = hero; } public void help() { String call = hero.call(); Log.d("callMessage", call); } } 复制代码
经过set方法注入对象。
基于注解注入:
Dagger2
中就用到了注解。因此这里用Dagger2
来实现一个了依赖注入的例子。再想象一个吃麻辣烫场景,麻辣烫里要加不少食材,好比牛肉、豆腐、香肠、鱼丸等而香肠又是由肠衣和肉馅组成,鱼丸是由鱼肉作成。因此代码通常是这样写:
public class TestActivity extends AppCompatActivity { Fish fish; FishBall fishBall; Doufu doufu; Potato potato; Meat meat; Casings casings; SeeYouTomrrow seeYouTomrrow; Sausage sausage; SpicyHotPot spicyHotPot; Beef beef; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); beef = new Beef(); fish = new Fish(); fishBall = new FishBall(fish); doufu = new Doufu(); potato = new Potato(); meat = new Meat(); casings = new Casings(); sausage = new Sausage(casings, meat); seeYouTomrrow = new SeeYouTomrrow(); spicyHotPot = new SpicyHotPot(potato, doufu, sausage, seeYouTomrrow, fishBall,beef); spicyHotPot.eat(); } } 复制代码
这里就是初始化了不少对象,其中有些对象中还引用了其余对象,像这样的初始化代码在日常开发中仍是比较常见的,而使用了Dagger2
就能够这样写:
public class TestActivity extends AppCompatActivity { @Inject SpicyHotPot spicyHotPot; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); DaggerTestComponent.builder().build().inject(this); spicyHotPot.eat(); } } 复制代码
使用了Dagger2
是否是简单了很多,只要一个注解加一行代码,就完成了多个对象的初始化,并且不管对象如何修改,这里的代码都无需变更,完成了解耦。
既然Dagger2
是经过注解实现的依赖注入,那么学习使用Dagger2
就是要学习Dagger2
中的注解的使用。不过,在具体看Dagger2
中的注解以前,先要在项目中引入Dagger2
的依赖,按照Github
上Dagger2的文档引入:
dependencies { implementation 'com.google.dagger:dagger:2.22.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' } 复制代码
接下来来看Dagger2
具体的注解。
@Inject
注解的做用主要有两个:
因此咱们要使用Dagger2
初始化一个类的依赖首先要在这个类的构造函数上加上@Inject
注解,而后在须要依赖的地方的对应变量上也加上@Inject
注解。可是光加上@Inject注解完成所谓的依赖注入吗?答案是否认的,@Inject
只是标注了依赖的需求方和依赖的提供方,可是它们俩之间尚未创建关系桥梁。而@Component
就是干这个的,具体来看下面这个例子:
public class Cola { @Inject public Cola() { } public String returnName() { return "百事可乐"; } } 复制代码
先定义一个可乐类,在他的构造函数上加上@Inject
注解,表示提供该类的依赖。再在Activity
中定义一个Cola
类型变量一样加上@Inject
注解,表示须要该类依赖:
public class InjectAndComponentActivity extends AppCompatActivity { private TextView mTextContent; @Inject Cola cola; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_inject_and_component); mTextContent = findViewById(R.id.textContent); } } 复制代码
接着新建一个接口ColaComponent
:
@Component
public interface ColaComponent {
void inject(InjectAndComponentActivity injectAndComponentActivity);
}
复制代码
接口上标注了@Component
注解,接着点击AndroidStudio上的Make Project
编译项目,此时Dagger2
会自动生成这个接口的实现类DaggerColaComponent
,由这个实现类的方法来完成依赖注入。接着在Activity
中经过实现调用他的inject
方法完成注入。
public class InjectAndComponentActivity extends AppCompatActivity { private TextView mTextContent; @Inject Cola cola; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_inject_and_component); mTextContent = findViewById(R.id.textContent); DaggerColaComponent.builder().build().inject(this); mTextContent.setText(cola.returnName()); } } 复制代码
注入以后就能够调用该对象的方法,运行效果:
上面举的麻辣烫的例子的实现也是同样的,每一个食材类的构造函数上加了@Inject
注解,建立一个Component
接口。贴上部分代码:
//肠衣 public class Casings { @Inject public Casings() { } } //肉 public class Meat { @Inject public Meat() { } } //香肠 public class Sausage { Casings casings; Meat meat; @Inject public Sausage(Casings casings, Meat meat) { this.casings = casings; this.meat = meat; } } //麻辣烫 public class SpicyHotPot { Potato potato; Doufu doufu; Sausage sausage; SeeYouTomorrow seeYouTomorrow; FishBall fishBall; Beef beef; @Inject public SpicyHotPot(Potato potato, Doufu doufu, Sausage sausage, SeeYouTomorrow seeYouTomorrow, FishBall fishBall, Beef beef) { this.potato = potato; this.doufu = doufu; this.sausage = sausage; this.seeYouTomorrow = seeYouTomorrow; this.fishBall = fishBall; this.beef = beef; } public void eat() { Log.d("Dagger2", "我开动了"); } } 复制代码
不管是3.1中Cola
类仍是以前的各类食材类都是咱们本身定义的类,因此能够本身修改,想使用Dagger2
只须要在类的构造函数上加上@Inject
注解。可是实际开发中会遇到使用三方类库的状况,这些三方类库中的类代码咱们没法修改,无法在其构造函数上加@Inject注解,那么是否是无法使用Dagger2
了呢?答案仍是否认的,Dagger2
中的@Module
和@Provides
注解就是用来处理只种状况。
看下面这个例子,这回不吃麻辣烫了,来吃炸鸡,因而定义了一个德克士类。
public class Dicos { String friedDrumstick; public Dicos() { friedDrumstick = "脆皮手枪腿"; } public String returnDicos() { return "德克士:" + friedDrumstick; } } 复制代码
这回不添加任何注解,就是一个正常的Dicos
对象类。接着一样新建一个DicosComponent
接口:
@Component
public interface DicosComponent {
void inject(ModuleAndProvidesActivity moduleAndProvidesActivity);
}
复制代码
接着在Activity
中定义Dicos
类型变量:
public class ModuleAndProvidesActivity extends AppCompatActivity { private TextView mTextContent; @Inject Dicos dicos; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_module_and_provides); mTextContent = findViewById(R.id.textContent); } } 复制代码
至此为止都和以前是同样的,接下来新建一个DicosModule
类,由于无法在三方类的构造函数上加@Inject
注解,因此要经过Module
类来提供依赖。
@Module public class DicosModule { @Provides public Dicos getDicos() { return new Dicos(); } } 复制代码
这里首先建立了一个DicosModule
类,并在DicosModule
类上加上@Module
注解,接着在这个类中只写了一个getDicos()
方法,调用Dicos
的构造方法建立对象而后返回。经过在方法上加上@Provides
注解,表示由这个方法为Dicos
类型提供依赖。最后记得还要在DicosComponent
接口注解上加上DicosModule
。这样在Activity
中@Inject
标记须要依赖时,才能找到。
@Component(modules = DicosModule.class)
public interface DicosComponent {
void inject(ModuleAndProvidesActivity moduleAndProvidesActivity);
}
复制代码
Make Project
后在Activity
中一样调用DaggerDiscosComponent
的inject
方法注入依赖便可。
public class ModuleAndProvidesActivity extends AppCompatActivity { private TextView mTextContent; @Inject Dicos dicos; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_module_and_provides); mTextContent = findViewById(R.id.textContent); DaggerDicosComponent.builder().dicosModule(new DicosModule()).build().inject(this); mTextContent.setText(dicos.returnDicos()); } } 复制代码
运行结果:
接下来考虑一下这种状况,若是一个类有多个构造方法,或者有两个相同依赖时,它们都继承同一个父类或者实现同一个接口,那么怎么区分呢?这就要用到@Named
或者@Qualifier
注解了。此次定义一个小肥羊火锅类。
public class LittleSheep { String mutton = "没有肉"; String vegetables = "没有菜"; public LittleSheep(String mutton) { this.mutton = mutton; } public LittleSheep(String mutton, String vegetables) { this.mutton = mutton; this.vegetables = vegetables; } public String retrunCotent() { return "小肥羊火锅:" + mutton + " " + vegetables; } } 复制代码
这个类有两个构造函数,接着一样是建立Component
和Module
类。
@Component(modules = LittleSheepModule.class)
public interface LittleSheepComponent {
void inject(NamedActivity namedActivity);
}
复制代码
@Module public class LittleSheepModule { @Named("all") @Provides public LittleSheep provideLittleSheepAll() { return new LittleSheep("十盘羊肉", "一盘蔬菜"); } @Named("mutton") @Provides public LittleSheep provideLittleSheepSingle() { return new LittleSheep("二十盘羊肉吃个够"); } } 复制代码
LittleSheepComponent
和以前的没什么区别,看到LittleSheepModule
,这个Module
有两个@Provides
注解方法,分别对应两个不一样传参的构造函数,可是这两个方法返回类型都是LittleSheep
都提供同一类型的依赖,在需求依赖的时候Dagger2
就分不清是哪一个了,因此这里要用@Named
注解来作个区分,这里分别设置两个不一样的name
,all
和mutton
作区分。除此以外在Activity
中,需求依赖的时候也要使用@Named
作区分。
public class NamedActivity extends AppCompatActivity { private TextView mTextContent; @Named("all") @Inject LittleSheep allLittleSheep; @Named("mutton") @Inject LittleSheep littleSheep; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_named); mTextContent = findViewById(R.id.textContent); DaggerLittleSheepComponent.builder().build().inject(this); mTextContent.setText(allLittleSheep.retrunCotent() + "\n" + littleSheep.retrunCotent()); } } 复制代码
NamedActivity
中一样在@Inject
注解上还要加上@Named
区分。运行结果:
接下来看继承同一个父类的状况,首先定义一个快餐类FastFood
,再定义他的两个子类KFC
和BurgerKing
。
public abstract class FastFood { public abstract String returnContent() ; } 复制代码
public class KFC extends FastFood { public KFC() { } @Override public String returnContent() { return "KFC全家桶"; } } 复制代码
public class BurgerKing extends FastFood { String beefBurger = "三层牛肉汉堡"; public BurgerKing() { } @Override public String returnContent() { return "汉堡王:" + beefBurger; } } 复制代码
接着仍是建立Component
和Module
类。
@Component(modules = FastFoodQualifierModule.class)
public interface FastFoodQualifierComponent {
void inject(FastFoodQualifierActivity fastFoodQualifierActivity);
}
复制代码
@Module public class FastFoodQualifierModule { @Provides public FastFood getKFC() { return new KFC(); } @Provides public FastFood getBurgerKing() { return new BurgerKing(); } } 复制代码
主要的问题仍是在Module
中,这样写仍是没法区分究竟是KFC
仍是BurgerKing
的依赖。这回不使用@Named
使用@Qualifier
处理。@Qualifier
比@Named
更加灵活和强大,用于自定义注解。接下来咱们定义两个注解。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface KFC {
}
复制代码
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BurgerKing {
}
复制代码
一个KFC
和一个BurgerKing
注解,存活时长都选择RUNTIME
,再添加上@Qualifier
注解限定,接下来就能够用这两个自定义注解来区分Module
中方法的类型。
@Module public class FastFoodQualifierModule { @com.sy.dagger2demo.annotations.KFC @Provides public FastFood getKFC() { return new KFC(); } @com.sy.dagger2demo.annotations.BurgerKing @Provides public FastFood getBurgerKing() { return new BurgerKing(); } } 复制代码
FastFoodQualifierModule
中两个方法上分别添加刚才的两个注解,接下来Activity
中和使用@Named
并没什么不一样,只是把@Named
注解分别换成刚才定义的注解。
public class FastFoodQualifierActivity extends AppCompatActivity { private TextView mTextContent; @KFC @Inject FastFood kfc; @BurgerKing @Inject FastFood burgerKing; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fast_food_qualifier); mTextContent = (TextView) findViewById(R.id.textContent); DaggerFastFoodQualifierComponent.builder().build().inject(this); mTextContent.setText(kfc.returnContent()+"\n"+burgerKing.returnContent()); } } 复制代码
运行结果:
@Singleton
看名字就知道这个注解是用来实现单例的。再次定义一个NetworkClient
类。采用@Module
和@Provides
注解实现依赖注入。
public class NetworkClient { String baseUrl; public NetworkClient() { baseUrl = "http://www.baidu.com/"; } public void init() { Log.d("NetworkClient", "网络初始化"); } } 复制代码
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
}
复制代码
@Module public class NetworkModule { @Provides public NetworkClient getClient() { return new NetworkClient(); } } 复制代码
public class SingletonAndScopeActivity extends AppCompatActivity { private TextView mContentTextView; @Inject NetworkClient networkClient1; @Inject NetworkClient networkClient2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_singleton_and_scope); mContentTextView = (TextView) findViewById(R.id.contentTextView); DaggerNetworkComponent.create().inject(this); mContentTextView.setText(networkClient1.toString() + "\n" + networkClient2.toString()); } } 复制代码
这里的代码和以前的彻底相同,只是在Activity
中添加了一个对象,同时有两个NetworkClient
对象,调用hashCode
方法查看是否为同一个对象。 运行结果:
能够看到这里hashCode
不一样,明显是两个对象。接下来使用@Singleton
实现单例。首先在NetworkComponent
上加上@Singleton
注解:
@Singleton
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
}
复制代码
接着在NetworkModule
中的方法上也加上@Singleton
注解:
@Module public class NetworkModule { @Singleton @Provides public NetworkClient getClient() { return new NetworkClient(); } } 复制代码
这样就ok了,就是这么简单,再次运行程序查看hashCode
。这时hashCode
相同已是同一个对象了。
运行结果:
注意这里其实@Singleton
实现的单例只是在同个Activity
下的单例,在其余Activity
下,再次建立这了类的对象就再也不是同一个对象了。这里咱们在新建一个Activity
测试下:
public class SecondActivity extends AppCompatActivity { private TextView mContentTextView; @Inject NetworkClient networkClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); mContentTextView = (TextView) findViewById(R.id.contentTextView); DaggerNetworkComponent.create().inject(this); mContentTextView.setText(networkClient.toString()); } } 复制代码
在Component
中添加inject
方法类型为SecondActivity
:
@Singleton
@Component(modules = NetworkModule.class)
public interface NetworkComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
复制代码
运行结果:
能够看到虽然使用了@Singleton
注解,可是也只能保证在同一个Activity
中是单例同一个对象,在多个Activity
中就没法保证了。那么怎么实现全局的单例呢?这能够用@Scope
注解。
@Scope
一样用来自定义注解用来限定注解,由于咱们知道Application
是单例的,因此可使用@Scope
结合Application
实现全局的单例模式。先定义一个新注解ApplicationScope
。
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
复制代码
这个和上面相似,只是用了@Scope
注解,接着修改以前的NetworkModule
,将@Singleton
换成新注解@ApplicationScope
:
@Module public class NetworkModule { @ApplicationScope @Provides public NetworkClient getClient() { return new NetworkClient(); } } 复制代码
接着建立一个新的ActivityComponent
:
@ApplicationScope
@Component(modules = NetworkModule.class)
public interface ActivityComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
复制代码
这里一样是使用了@ApplicationScope
注解,接着建立MyApplicatin
类在其中去获取ActivityComponent
的实例。
public class MyApplication extends Application { ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); activityComponent = DaggerActivityComponent.builder().build(); } public static MyApplication getApplication(Context context){ return (MyApplication) context.getApplicationContext(); } public ActivityComponent getActivityComponent(){ return activityComponent; } } 复制代码
这里看到在MyApplication
中经过Dagger2
获取到ActivityComponet
的实例再给出public
方法获取这个ActivityComponent
实例。这样在Activity
中就能够经过这个ActivityComponent
注入,得到单例的对象了。修改Activity
中代码:
public class SingletonAndScopeActivity extends AppCompatActivity { private TextView mContentTextView; @Inject NetworkClient networkClient1; @Inject NetworkClient networkClient2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_singleton_and_scope); mContentTextView = (TextView) findViewById(R.id.contentTextView); // DaggerNetworkComponent.create().inject(this); //经过Application中的Component注入 MyApplication.getApplication(this).getActivityComponent().inject(this); mContentTextView.setText(networkClient1.hashCode() + "\n" + networkClient2.hashCode()); mContentTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(SingletonAndScopeActivity.this,SecondActivity.class)); } }); } } 复制代码
public class SecondActivity extends AppCompatActivity { private TextView mContentTextView; @Inject NetworkClient networkClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); mContentTextView = (TextView) findViewById(R.id.contentTextView); // DaggerNetworkComponent.create().inject(this); //经过Application中的Component注入 MyApplication.getApplication(this).getActivityComponent().inject(this); mContentTextView.setText(networkClient.hashCode()+""); } } 复制代码
运行结果:
再次运行查看结果,发现这是已经全是同一个对象了。
Component
还能够经过dependencies
依赖于别的Component
。这里再从新定义一个PizzaHut
类:
public class PizzaHut { String SuperSupremePizza = "超级至尊披萨"; public PizzaHut() { } public String returnContent() { return "必胜客超级至尊套餐:" + SuperSupremePizza; } } 复制代码
@Module public class PizzaHutModule { @Provides public PizzaHut getPizzaHut() { return new PizzaHut(); } } 复制代码
@Component(modules = PizzaHutModule.class)
public interface PizzaHutComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
复制代码
同时还建立了对应的Module
和Componet
,仍是和以前没什么区别。接下来在ActivityComponent
中使用dependencies
将PizzaHutComponent
引入。
@ApplicationScope
@Component(modules = NetworkModule.class,dependencies = PizzaHutComponent.class)
public interface ActivityComponent {
void inject(SingletonAndScopeActivity singletonAndScopeActivity);
void inject(SecondActivity secondActivity);
}
复制代码
接着到MyApplication
中引入PizzaHutComponent
:
public class MyApplication extends Application { ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); activityComponent = DaggerActivityComponent.builder().pizzaHutComponent(DaggerPizzaHutComponent.builder().build()).build(); } public static MyApplication getApplication(Context context) { return (MyApplication) context.getApplicationContext(); } public ActivityComponent getActivityComponent() { return activityComponent; } } 复制代码
最后再到刚才的Activity
中添加一个PizzaHut
类型变量:
public class SingletonAndScopeActivity extends AppCompatActivity { private TextView mContentTextView; @Inject NetworkClient networkClient1; @Inject NetworkClient networkClient2; @Inject PizzaHut pizzaHut; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_singleton_and_scope); mContentTextView = (TextView) findViewById(R.id.contentTextView); // DaggerNetworkComponent.create().inject(this); MyApplication.getApplication(this).getActivityComponent().inject(this); mContentTextView.setText(networkClient1.hashCode() + "\n" + networkClient2.hashCode()+"\n"+pizzaHut.returnContent()); mContentTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(SingletonAndScopeActivity.this,SecondActivity.class)); } }); } } 复制代码
运行结果:
能够看到这里已经成功注入PizzaHut
,而且调用了returnContent
方法。
Dagger2
也支持懒加载模式,就是@Inject
的时候不初始化,而到使用的时候调用get
方法获取实例。
public class SingletonAndScopeActivity extends AppCompatActivity { private TextView mContentTextView; @Inject NetworkClient networkClient1; @Inject NetworkClient networkClient2; @Inject Lazy<PizzaHut> pizzaHut; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_singleton_and_scope); mContentTextView = (TextView) findViewById(R.id.contentTextView); // DaggerNetworkComponent.create().inject(this); MyApplication.getApplication(this).getActivityComponent().inject(this); //使用的时候调用get方法获取处理 mContentTextView.setText(networkClient1.hashCode() + "\n" + networkClient2.hashCode()+"\n"+pizzaHut.get().returnContent()); mContentTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(SingletonAndScopeActivity.this,SecondActivity.class)); } }); } } 复制代码
在对Dagger2
中的注解有了必定的了解以后,继续学习Dagger2+MVP
。将Dagger2
与MVP
结构结合起来,可使MVP
架构中的依赖更加清晰更加易于管理。学习MVP+Dagger2
天然是去看Google
官方提供的Demo
。
先看一下工程目录:
MVP
的
Presenter
、
Contract
、
Fragment
等类以外,还有
Component
和
Module
这些类都是使用了
Dagger2
会用到的。接着先来具体看
ToDoApplication
类:
public class ToDoApplication extends Application { private TasksRepositoryComponent mRepositoryComponent; @Override public void onCreate() { super.onCreate(); mRepositoryComponent = DaggerTasksRepositoryComponent.builder() .applicationModule(new ApplicationModule((getApplicationContext()))) .tasksRepositoryModule(new TasksRepositoryModule()).build(); } public TasksRepositoryComponent getTasksRepositoryComponent() { return mRepositoryComponent; } } 复制代码
看到这里就是构建了一个TasksRepositoryComponent
,并提供了一个得到的方法。先找到TaskRepositoryComponent
:
@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
TasksRepository getTasksRepository();
}
复制代码
首先看到这里用了单例的注解,接着看到有两个Module
,还提供了一个获取TaskRepository
的方法,这个TaskRepository
是用来获取数据的,在Presenter
构造中传入,Presenter
调用其中方法得到数据。先来看TaskRepositoryModule
:
@Module public class TasksRepositoryModule { @Singleton @Provides @Local TasksDataSource provideTasksLocalDataSource(Context context) { return new TasksLocalDataSource(context); } @Singleton @Provides @Remote TasksDataSource provideTasksRemoteDataSource() { return new FakeTasksRemoteDataSource(); } } 复制代码
看到这个Module
中有两个@Provides
标注的方法,是用来提供测试数据返回的。一个方法是本地数据,一个是模拟远程数据。返回的都是TasksDataSource类型,因此用了自定义注解@Local
和@Remote
作了区分。点进去看这两个注解,其定义时都用了@Qualifier
注解。
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Local {
}
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {
}
复制代码
接着返回看ApplicationModule
:
@Module public final class ApplicationModule { private final Context mContext; ApplicationModule(Context context) { mContext = context; } @Provides Context provideContext() { return mContext; } } 复制代码
看到其中只提供了一个上下文的mContext
方法。回到ToDoApplication
中看到这里建立ApplicationModule
传入的是ApplicationContext
。下面进入tasks
这个具体模块页面查看Dagger2
具体是怎么和MVP
结合的。
public class TasksActivity extends AppCompatActivity { private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY"; private DrawerLayout mDrawerLayout; @Inject TasksPresenter mTasksPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); ...... TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); } // Create the presenter DaggerTasksComponent.builder() .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent()) .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build() .inject(this); ...... } ...... } 复制代码
这段是TasksActivity
中的代码,其中省略掉了一些无关的代码。能够看到这和Google
官方MVP
的Demo
同样,是在Activity
里放了一个Fragment
将Fragment
做为View
使用。看到这里在TasksPresenter
上加了@Inject
注解,也就是说这里是要用Dagger2
初始化Presenter
。在onCreate
方法中经过DaggerTasksComponent
的inject
方法注入TasksPresenter
依赖,建立TasksPresenter
。接下来来看TasksComponent
接口的代码:
@FragmentScoped
@Component(dependencies = TasksRepositoryComponent.class, modules = TasksPresenterModule.class)
public interface TasksComponent {
void inject(TasksActivity activity);
}
复制代码
其中除了设置了TasksPresenterModule
并且还依赖了TasksRepositoryComponent
这个Component
。这就让以前的TasksRepository
在这里也可使用。接着进入TasksPresenterModule
查看:
@Module public class TasksPresenterModule { private final TasksContract.View mView; public TasksPresenterModule(TasksContract.View view) { mView = view; } @Provides TasksContract.View provideTasksContractView() { return mView; } } 复制代码
TasksPresenterModule
中标注了mView
的@Provides
方法,为注入View
提供了方法。最后看到TasksPresenter
:
final class TasksPresenter implements TasksContract.Presenter { private final TasksRepository mTasksRepository; private final TasksContract.View mTasksView; private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS; private boolean mFirstLoad = true; @Inject TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) { mTasksRepository = tasksRepository; mTasksView = tasksView; } @Inject void setupListeners() { mTasksView.setPresenter(this); } ...... } } 复制代码
TasksPresenter
的构造方法上加上@Inject
注解提供了依赖。至此全部对象具能由Dagger2
提供依赖,在TaskActivity
注入依赖,完成了Dagger2
与MVP
的结合,完成了解耦。
DaggerTasksComponent.builder()
.tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
.tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
.inject(this);
复制代码
1. 使用Dagger2的目的是解耦。使用Dagger2更好更清晰地管理项目中类之间的依赖。
2. 关于Dagger2的使用个人理解是: