1、前言
Dagger2作为一个上手难度较高的框架,我也是看了许多相关的文章,经历了无数次的从入门到放弃。放弃的多了好像也有一点懂了,于是乎我也总结一下自己对Dagger2使用的相关知识的理解。
2、依赖注入
关于Dagger2首先要理解的就是依赖注入(DI)和控制反转(IOC),对这两个概念你如果已经有所了解,可以直接跳到下一节。
在理解依赖注入之前先了解依赖注入的目的,也是使用Dagger2框架的目的,知道了目的才能更好地理解过程。依赖注入的目的就是二个字:解耦。
高内聚,低耦合,是面向对象编程提倡遵循的设计原则,而通过依赖注入的方式能实现控制反转,从而实现解耦的目的。
光这样说还是太理论了,不易理解,举个例子来帮助理解下,最近复联4大火,举个漫威英雄的例子。
首先想象一下,你是个自带柯南属性普通人,每次有外星人入侵或者是超能力变种人搞破坏,你都好巧不巧的能出现在现场,然而你并没有能力打败他们,你只有托尼史塔克的联系方式,所以你每次都联系他,由他来解决这些麻烦。然而托尼突然有一天告诉你他要带着小辣椒去度假,会有一段时间联系不上。你没办法出于求生本能,你得去寻找另一个超级英雄,你找到了美国队长,队长一口答应,说没问题,下次有事联系我,我来搞定。于是你获得了美国队长的联系方式。又过一段时间,队长也和你说他要和冬兵去度假,然而托尼还度假没回来。你没办法,又只能自己去找寡姐,获得了他的联系方式,下次遇到袭击就联系她。发现了没有,这里你每次都依赖与某一个超级英雄,一旦发生变故,你只能自己去找新的英雄获得更新他的联系方式。这样对某个英雄依赖非常严重,现在换一种方法,不具体依赖某个英雄,我直接去神盾局找尼克弗瑞得到他的联系方式,以后有事都联系他,至于是哪个超级英雄来解决又或者怎么去联系英雄,我都不用知道,交给尼克弗瑞去处理就行。
下面通过代码加深理解,先定义三个具体英雄类:
//抽象英雄类
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 "黑寡妇:我已收到您的求救消息,正在赶来的路上";
}
}
最后是自己类:
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();
执行日志:
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实例化一个英雄类,修改后变为被动通过调用第三方类方法注入一个英雄类,由主动到被动实现了控制反转,实现了解耦,达成了这个目的。
关于依赖注入的方法有以下几种:
- 基于接口注入
- 基于构造函数注入
- 基于 set 方法注入
- 基于注解注入
基于接口:
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是不是简单了不少,只要一个注解加一行代码,就完成了多个对象的初始化,而且无论对象如何修改,这里的代码都无需变动,完成了解耦。
3、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具体的注解。
3.1 @Inject和@Component注解
@Inject注解的作用主要有两个:
- 一是标注在成员变量上,表示需要通过Dagger2为它提供依赖。
- 二是标注在构造函数上,表示为这个类型的成员变量提供依赖。
|所以我们要使用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.2 @Module和@Provides注解
无论是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());
}
}
运行结果:
3.3 @Named和@Qualifier注解
接下来考虑一下这种情况,如果一个类有多个构造方法,或者有两个相同依赖时,它们都继承同一个父类或者实现同一个接口,那么怎么区分呢?这就要用到@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());
}
}
运行结果:
3.4 @Singleton和@Scope注解
@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;
}
}
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;
}
}
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()+"");
}
}
运行结果:
再次运行查看结果,发现这是已经全是同一个对象了。
3.5 @Component的dependencies
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方法。
3.6 懒加载
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));
}
});
}
}
4、Dagger2与MVP
在对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);
读者福利
Android架构师的门槛,有没有免费学习资料?