看了很多关于Dagger2的中文用法介绍,看过之后的感受一言难尽。
这里是几篇相对好一些的。
https://blog.csdn.net/mq2553299/article/details/77485800
https://www.jianshu.com/p/65737ac39c44
https://www.jianshu.com/p/22c397354997
下面的链接是一篇Dagger2英文android教学,比大部分中文的要好很多。(需要翻墙)
https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-introduction-be6580cb3edb
顺便附上Dagger2的官网和代码(包括example)以及google的官方demo
https://google.github.io/dagger/users-guide
https://github.com/google/dagger
https://github.com/googlesamples/android-architecture/tree/todo-mvp-dagger
简单的说下思路
理解依赖注入(DI)
A类中有B类的对象,就叫A类依赖B类,B类是A类的依赖。
这里有两个War类,他们有不同的构造方法。
public class War {
private Starks starks;
private Boltons boltons;
public War(){
starks = new Starks();
boltons = new Boltons();
starks.prepareForWar();
starks.reportForWar();
boltons.prepareForWar();
starks.reportForWar();
}
}
public class War {
private Starks starks;
private Boltons boltons;
//DI - getting dependencies from else where via constructor
public War(Starks starks, Boltons bolton){
this.starks = starks;
this.boltons = bolton;
}
public void prepare(){
starks.prepareForWar();
boltons.prepareForWar();
}
public void report(){
starks.reportForWar();
boltons.reportForWar();
}
}
对于第一种构造方法,如果Starks和Boltons类的构造方法发生变化,需要修改War类中的代码,耦合严重。
对于第二种构造模式,在实例化war对象时需要提供一个Starks对象和Boltons对象,这个过程——将依赖注入的过程在程序的主方法中实现。
public class BattleOfBastards {
public static void main(String[] args){
Starks starks = new Starks();
Boltons boltons = new Boltons();
War war = new War(starks,boltons);
war.prepare();
war.report();
}
}
在android开发过程中,这个过程在Activity的onCreate(Bundle savedInstanceState)方法和Application的onCreate()方法以及Fragment的onAttach(Context context)方法中实现。
(该内容来自英文教学,中文的我没看到过)
(这是Activity和Fragment生命周期的第一个方法,避免出现对象为null的错误)
Dagger2的作用就是通过一系列工厂方法实例化依赖注入所需要的对象。
具体实现过程
建议查看英文教程
https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-introduction-be6580cb3edb
要记住Dagger2的实现思想——在实例化一个对象的时候将它所需要的依赖注入其中。这里有三个基本问题,谁是依赖类,被依赖的对象需要注入到哪里去,具体注入方式。重点就在于具体注入方式。
注意其编写代码的思路
1、Top @Component
2、@Module
3、连接Modules
4、关联Component和Module
5、编译和注入
这和Dagger2官网的User Guide中的思路基本相反
声明依赖,满足依赖,建立图表
剩下的则是注意懒加载,单例和范围限定符等常用注解。
关于接口和MVP模式
在MVP模式中,一般会有一个Contract接口,该接口中有Presenter和View两个接口,我们需要实现这两个接口,并分别在接口的实现类中调用另一个接口的方法。为了解耦在实现类中,我们声明的是接口而不是实现类。
我们先看Dagger2官方example中是如何声明接口依赖的。
这个是第一种声明方法(在构造方法中使用了@Inject)。Pump为接口,PumpModule是Module类,Thermosiphon是接口的实现类。
interface Pump {
void pump();
}
@Module
abstract class PumpModule {
@Binds
abstract Pump providePump(Thermosiphon pump);
}
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
@Override public void pump() {
if (heater.isHot()) {
System.out.println("=> => pumping => =>");
}
}
}
在声明的过程中,@Inject标注了Thermosiphon类的构造方法,抽象PumpModule类中用@Binds标注了抽象方法providePump(Thermosiphon pump),该方法返回接口Pump。在最终的使用过程中该方法并没有使用,该方法是用于编译时生成代码的绑定声明。
这个是第二种声明方法(使用Module类)。
@Module(includes = PumpModule.class)
class DripCoffeeModule {
@Provides
@Singleton
Heater provideHeater() {
return new ElectricHeater();
}
}
class ElectricHeater implements Heater {
boolean heating;
@Override public void on() {
System.out.println("~ ~ ~ heating ~ ~ ~");
this.heating = true;
}
@Override public void off() {
this.heating = false;
}
@Override public boolean isHot() {
return heating;
}
}
interface Heater {
void on();
void off();
boolean isHot();
}
需要注意的就是DripCoffeeModule 类中唯一的方法,声明的返回类型是接口,返回了一个新的ElectricHeater对象。
在谷歌的官方example中,Activity管理Fragment的生命周期,Fragment用于展示数据,Presenter被注入到了Fragment里,Fragment被注入到Activity中。官方example使用了Dagger-Android组件,我们暂时先不管这个。以TaskDetail为例,可以看到TaskDetailPresenter的构造方法被@Inject标注,这是第一种声明方法。在TaskDetailPresenterModule 类中有抽象方法statitsticsPresenter(TaskDetailPresenter presenter),这和之前提到的是一样的。
final class TaskDetailPresenter implements TaskDetailContract.Presenter {
...
@Inject
TaskDetailPresenter(@Nullable String taskId,
TasksRepository tasksRepository) {
mTasksRepository = tasksRepository;
mTaskId = taskId;
}
...
}
@Module
public abstract class TaskDetailPresenterModule {
@FragmentScoped
@ContributesAndroidInjector
abstract TaskDetailFragment taskDetailFragment();
@ActivityScoped
@Binds
abstract TaskDetailContract.Presenter statitsticsPresenter(TaskDetailPresenter presenter);
@Provides
@ActivityScoped
static String provideTaskId(TaskDetailActivity activity) {
return activity.getIntent().getStringExtra(EXTRA_TASK_ID);
}
}
这里另外要注意的是statitsticsPresenter方法是被@ActivityScoped标注的,这意味着它的生命周期应该和Activity一致。