Dagger2定义
Dagger2 是一个Android依赖注入框架,由谷歌开发,最早的版本Dagger1 由Square公司开发。依赖注入框架主要用于模块间解耦,提高代码的健壮性和可维护性。Dagger 这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理。Jake Wharton 在对 Dagger 的介绍中指出,Dagger 即 DAG-er,这里的 DAG 即数据结构中的 DAG——有向无环图(Directed Acyclic Graph)。也就是说,Dagger 是一个基于有向无环图结构的依赖注入库,因此Dagger的使用过程中不能出现循环依赖。
那什么是依赖呢?如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。
那什么又是依赖注入呢,依赖注入就是非自己主动初始化依赖,而通过外部来传入依赖的方式,简单来说就是不使用 new 来创建依赖对象。使用 Dagger2 创建依赖对象,我们就不用手动初始化了。个人认为 Dagger2 和 MVP 架构是比较不错的搭配,Activity 依赖的 Presenter 可以使用该DI框架直接生成,实现解耦,简单的使用方式如下:
dagger2到底有哪些好处?
1.增加开发效率、省去重复的简单体力劳动
首先new一个实例的过程是一个重复的简单体力劳动,dagger2完全可以把new一个实例的工作做了,因此我们把主要精力集中在关键业务上、同时也能增加开发效率上。
省去写单例的方法,并且也不需要担心自己写的单例方法是否线程安全,自己写的单例是懒汉模式还是饿汉模式。因为dagger2都可以把这些工作做了。
2.更好的管理类实例
每个app中的ApplicationComponent管理整个app的全局类实例,所有的全局类实例都统一交给ApplicationComponent管理,并且它们的生命周期与app的生命周期一样。
每个页面对应自己的Component,页面Component管理着自己页面所依赖的所有类实例。
因为Component,Module,整个app的类实例结构变的很清晰。
3.解耦
假如不用dagger2的话,一个类的new代码是非常可能充斥在app的多个类中的,假如该类的构造函数发生变化,那这些涉及到的类都得进行修改。设计模式中提倡把容易变化的部分封装起来。
假如是通过用Inject注解标注的构造函数创建类实例,则即使构造函数变的天花乱坠,我们基本上都不需要修改任何代码。
假如是通过工厂模式Module创建类实例,Module其实就是把new类实例的代码封装起来,这样即使类的构造函数发生变化,只需要修改Module即可。
4.利于分工和维护拓展,只需提供接口文档,调用者无需关心构造函数参数
配置
在 APP gradle 配置文件中
由于 Dagger 使用 apt 生成代码,在Project gradle中还需要加入:
结构
Dagger2主要分为4部分:
对象,被调用者, 被依赖注入的类,一般为mvp模式中的p,即presenter
对象的实例化,叫做容器Module
沟通桥梁:将容器和调用者连接起来Component
调用者:需要实例化对象的类,即activity、fragment
对象
public class Person {
@Inject
public Person(){
Log.i("dagger","person create!!!");
}
}
容器
@Module //提供依赖对象的实例
public class MainModule {
@Provides // 关键字,标明该方法提供依赖对象
Person providerPerson(){
//提供Person对象
return new Person();
}
}
桥梁
@Component(modules = MainModule.class) // 作为桥梁,沟通调用者和依赖对象库
public interface MainComponent {
//定义注入的方法
void inject(MainActivity activity);
}
调用者
public class MainActivity extends AppCompatActivity{
@Inject //标明需要注入的对象
Person person;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 构造桥梁对象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
}
}
解释下连接桥梁初始化
MainComponent是定义的桥梁,build后会build目录自动生成DaggerMainComponent类,类名由Dagger和MainComponent组成,接下来的.mainModule 方法名也不是固定的,
是由MainComponent中modules = MainModule.class决定的。总之桥梁的实例化是会根据桥梁的类名和类里面依赖的父类、使用的容器多个因素决定的。
DaggerMainComponent类
MainComponent
逻辑描述
1. 在Application构建全局component,app初始化即执行
注意要在AnroidMainfest.xml中给application标明执行的类名,即 android:name=".app.RXRetrofitApplication"
全局AppComonent里面注明连接的容器以及向子容器传递的对象,inject表示调用者
如上所示,向子桥梁传递了MainViewInteraction对象,这个对象见InteractorModule:
2. 子桥梁 继承主桥梁(AppComonent),在子桥梁里面的容器MainActivityModule可以使用主桥梁传递过来的对象
依赖注入有个特性,被依赖注入的对象若有参数传递,参数也必须依赖注入,MainViewInteraction有参数WeatherApiService, 依赖注入WeatherApiService, 然后再依赖注入Retrofit,Retrofit里return RestApiAdapter.getInstance(), RestApiAdapter.getInstance()即为Retrofit网络访问相关逻辑。
3. Activity依赖注入Presenter对象,构建本activity的桥梁
MainActivityComponent继承appComponent,连接mainActivityModule
,并且将本activity作为参数传进去。在mainActivityModule里标注了MainViewPresenter,执行new MainViewPresenterImpl(mainView, mainViewInteraction),本activity 赋值给了mainView,回调mainView接口中的showWeatherInfor,mainViewInteraction参数封装了Retrofit
。
当点击text,则把参数传给mainViewPresenter
由于load方法加了监听,会回调onFinished
方法,执行showWeatherInfor方法,就把指定地点的天气信息显示出来
注解解释
@Module:作为实例对象的容器。
@Provides:标注能够提供实例化对象的方法。
@Component:作为桥梁,注入对象的通道。
@Inject:需要注入的方法
调用者使用@Inject标识被调用者, 并构建桥梁。
桥梁使用@Component, 并标识依赖的父桥梁、连接的容器、子桥梁、将连接的容器中指定的方法传给子桥梁中的容器几个因子。
容器使用@Module,提供依赖对象@Provides
被调用者使用@Inject,表明被调用的方法
其中容器和被调用者设计的@Provides和@Inject,是先搜索容器中@Provides标示的对象,如果没有,再去搜索@Inject,个人觉得都写上更好点
@Singleton当前提供的对象将是单例模式 ,一般配合@Provides一起出现,当module出现@Singleton,其对应的Component也必须标注Singleton。
@Provides // 关键字,标明该方法提供依赖对象
@Singleton
Person providerPerson(){
return new Person();
}
@Named 对象有多个构造函数,通过@named区分使用哪个。
@Scope作用域注解,通过自定义注解限定注解作用域。
比如@Singleton就是Dagger预先定义的作用域注解。
当有Component依赖于别的Component的情况时,两者的作用域不能相同,必须定义一个新的作用域,在同一Component里面标记的多个activity 依赖注入一个类使用的是同一个对象,如果两个activity分别使用各自的Component对同一个类标记不同的作用域,则引用的是不同的对象。
@Subcomponent 继承,Subcomponent其功能效果优点类似component的dependencies。但是使用@Subcomponent不需要在父component中显式添加子component需要用到的对象,只需要添加返回子Component的方法即可,子Component能自动在父Component中查找缺失的依赖。
//父Component:
@PerApp
@Component(modules=××××)
public AppComponent{
SubComponent subcomponent(); //1.只需要在父Component添加返回子Component的方法即可
}
//子Component:
@PerAcitivity //2.注意子Component的Scope范围小于父Component
@Subcomponent(modules=××××) //3.使用@Subcomponent
public SubComponent{
void inject(SomeActivity activity);
}
//使用
public class SomeActivity extends Activity{
public void onCreate(Bundle savedInstanceState){
...
App.getComponent().subCpmponent().inject(this);//4.调用subComponent方法创建出子Component
}
}
个人感受
好处:
1.解耦,将依赖的对象实例集中在容器中,供activity或者fragment调用,一般情况会有多个地方访问同一对象,对象构造函数发生变化,只需在容器中修改即可。
2.调用者不需要关心依赖注入对象的初始化,只需关心该对象提供什么功能,使用了相应的什么函数。
3.提供单例模式,使用@singleton就搞定了。
4.结合mvp使用,进一步进行了解耦,利于测试和维护。
缺点:
1.工作量变大,相当于把一个简单的new过程 分解了桥梁、容器、调用与被调用者,还有多个注解,目前没发现这种方式提高了性能的说法。
2.主要是对P层依赖注入,也就是说如果不是mvp架构,dagger就没多大意义了。
相关链接
Demo下载
https://github.com/tuozhaobing/RXRetrofitDaggerMvpDemo.git
https://github.com/niuxiaowei/Dagger2Sample.git
资料学习
http://www.cnblogs.com/zhuyp1015/p/5119727.html
http://blog.youkuaiyun.com/u014315849/article/details/51566388
http://www.jianshu.com/p/a23c50cb4094