dagger2的缘由背景
dagger2除了也有一段时间了,但是现在各大开源项目都在使用它,虽然感觉他的用处并不是很大,但是看了几篇文章,现在写一篇文章来记录一下东西,方便以后查阅。先来看看dagger2的使用背景。
public class TestPresenter extends BasePresenter<TestView> {
A a;
B b;
public TestPresenter(A a,B b) {
this.b = b;
this.a = a;
}
}
那么我们在实例化这个Presenter的时候就需要这样做
A a = new A();
B b = new B();
需要把TestPresenter需要的参数一个个的实例化,然后作为参数传给它。试想一下,如果我们的A和B 还需要很多参数,或者TestPresenter需要3个乃至更多的参数,那我们实例化一个TestPresenter要做的操作是不是太多了,这样实在是太夸张了,我们只是需要一个TestPresenter实例而已,就必须知道TestPresenter的Dependency是什么,TestPresenter的Dependency的Dependency是什么。。。首先这样做操作很是繁琐,其次,这样做,导致代码耦合程度非常高,我们改变其中一环的时候,就要做大量的代码改动。
然而,如果我们使用dagger2,这些问题就可以迎刃而解了。dagger2用一个类似依赖工厂的东西,将所需的依赖统一管理起来,所有需要用到依赖的类都可以到这里寻找相应的依赖,并且dagger2会自动搜索这个类所用到的依赖的依赖,系统会自动识别这个依赖关系。
使用dagger2的好处
1.增加开发效率
2.更好地管理类实例
3.解耦
注解属性
先看看各个注解的所用属性,这里纯粹当做笔记看下就行。先了解个概念,后续不懂的时候可以在这里查看一下。
-Dagger2 通过注解来生成代码,定义不同的角色,主要的注解如下:
- @Module: Module类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。
- @Provides: 在Module中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
- @Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
- @Component: Component从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。将Module中产生的依赖对象自动注入到需要依赖实例的Container中。
- @Scope: Dagger2可以通过自定义注解限定注解作用域,来管理每个对象实例的生命周期。
- @Qualifier: 当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 qualifier注解“@perApp”和“@perActivity”,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。
Dagger2的使用步骤
1.导入方法:
- 在整个项目的build.gradle中加入:
dependencies {
// other classpath definitions here
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
- 在app/build.gradle中分别加入:
// add after applying plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt'
· dependencies {
// apt command comes from the android-apt plugin
apt 'com.google.dagger:dagger-compiler:2.2'
compile 'com.google.dagger:dagger:2.2'
provided 'javax.annotation:jsr250-api:1.0'
}
2.注解的三个分类
- 构造器注入@Inject标注在构造器上其实有两层意思。
①告诉Dagger2可以使用这个构造器构建对象。
②注入构造器所需要的参数的依赖。
构造器注入的局限:如果有多个构造器,我们只能标注其中一个,无法标注多个。 - 属性注入
如MainActivity类,标注在属性上。被标注的属性不能使用private修饰,否则无法注入。
属性注入也是Dagger2中使用最多的一个注入方式。 - 方法注入
public class MainActivity extends AppCompatActivity {
private A a;
@Inject
public void setA(A a) {
this.a= a;
}
}
标注在public方法上,Dagger2会在构造器执行之后立即调用这个方法。
比如google mvp dagger2中,给View设置Presenter的时候可以这样使用方法注入。
其中构造器注入和属性注入一般是成对使用,而且一般使用方法基本上就是他们。方法注入一般用的比较少。在上面已经介绍过。
使用示例
先来看一个没有使用module提供类的,最简单的dagger2的是使用示例
public class A{
@Inject
B b;
public A() {
}
}
public class B{
@Inject
public B() {
}
}
通过这样, 在A类中就初始化了变量B。
对于上面这种方式,我是可以在对应的目标类上是用inject来标注它的构造方法的,那当我们使用第三方的时候呢?这时候我们并不能修改他的方法,这就引入了module,module其实就是提供变量的一个管理类差不多,里面都是一些通过提供构造方法来获得变量的,然后通过component这个注入器将里边的变量注入到使用类中。一种图可以来简化他们的关系。
再来看一个使用了module提供目标类的使用,基于MVP模式的。用于初始化prsenter这个变量。
module类:
@Module
public class AppModule {
TestView testView;
public AppModule(TestView testVIew){
this.TestView = testView
}
@Provides
public TestPresenter providePresenter() {
return new TestPresenter(testView);
}
}
两个接口类:
public interface TestView{//基于View接口层
void showToast();
}
public interface BasePresenter{//Presnter的接口层
void loadData();
}
看一下presenter的代码:
public class TestPresenter extends BasePresenter{
TestView testView;
public TestPresenter(TestView testView) {
this.testView = testView;
}
@Override
public void loadData(){
...
testView.showToast();
}
}
再来定义我们的Componment,定义Componment的两种方式:
@Component(modules = {AppModule.class})
public interface AppComponent {
TestPresenter testPresenter();
}
@Component(modules = AppModule.class)
public interface AppComponent {
TestActivity inject(TestActivity activity);
}
对于这种方式:Component显示暴露了调用方法,我们在activity中是这样初始化并使用的。
public TestActivity extend Activity{
@Inject
TestPresenter testPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
mTestPresenter= appComponent.testPresenter();
}
@Override
public void showToast(){
......
}
}
对于第二种方式,是这样使用的,通常情况下这种方式使用的概率会比较大。
public TestActivity extend Activity imlpments TestView{
@Inject
TestPresenter testPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
appComponent.inject(this);
}
@Override
public void showToast(){
......
}
}
上面就是dagger2结合MVP模式的经典使用了,明白了基础方法的使用,可以看看dagger2的其它注解属性的使用。
@qualifier的作用
当目标类同时有两个构造函数的时候,而且同时被inject标注了,这时候就犯难了,使用类不知道使用哪一个。这时候qualifier的使用就来了。
public class Fruit{
private String name;
public Fruit(String name) {
this.name = name;
}
public Fruit(){
name = "苹果";
}
}
这里自定义一个qualifier注解
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonQualifier {
}
FruitModule
@Module
public class FruitModule {
//一个默认的
@Provides
Fruit provideFruit() {
return new Fruit();
}
//采用@Qualifier注解,表示我可以提供这种标识符的Fruit
@Provides
@Named("banana")
Fruit provideFruit01() {
return new Fruit("香蕉");
}
@Provides
@Named("xueli")
Fruit provideFruit02() {
return new Fruit("雪梨");
}
@Provides
@PersonQualifier
Fruit provideFruitByQualifier() {
return new Fruit("qualifier Fruit");
}
}
然后我们在需要注解的Activity上看使用
@Inject
Fruit mFruit;
//这么多对象,如果需要特定的对象,用@Qualifier标识符注解,@Named是自定义的一个标识符注解
@Inject
@Named("banana")
Fruit mBanana;
@Inject
@Named("xueli")
Fruit mXueli;
@Inject
@FruitQualifier
Fruit mFruitQualifier;
@Scope和@Singleton
@Scope是用来管理依赖的生命周期的。它和@Qualifier一样是用来自定义注解的,而@Singleton则是@Scope的默认实现。
注意点:
@Scope是需要成对存在的,在Module的Provide方法中使用了@Scope,那么对应的Component中也必须使用@Scope注解
实际上@singleton并不是说标注了这个类就是单例类了。那要dagger2要实现单例。需要什么条件呢?
依赖在Component中是单例的(供该依赖的provide方法和对应的Component类使用同一个Scope注解。)
对应的Component在App中只初始化一次,每次注入依赖都使用这个Component对象。(在Application中创建该Component)
下面来看看实现@singleton的示例代码:
@Module
public class ApplicationModule {
private App mApplication;
public ApplicationModule(App application) {
mApplication = application;
}
@Provides
@Singleton
@ContextLife("Application")
public Context provideApplicationContext() {
return mApplication.getApplicationContext();
}
}
ApplicationComponent.java
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
@ContextLife("Application")
Context getApplication();
}
// 单例的有效范围是整个Application
public class App extends Application {
private static ApplicationComponent mApplicationComponent; // 注意是静态
public void onCreate() {
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
// 对外提供ApplicationComponent
public static ApplicationComponent getApplicationComponent() {
return mApplicationComponent;
}
}
当然也可以自定义@scope
可以看到定义一个Scope注解,必须添加以下三部分:
- @Scope :注明是Scope
- @Documented :标记文档提示
- @Retention(RUNTIME) :运行时级别
对于Android,我们通常会定义一个针对整个APP全生命周期的@PerApp的Scope注解和针对一个Activity生命周期的@PerActivity和singletop的模式差不多,这里就不多说了。
组织component
- 依赖:@Component的dependencies属性
- 包含:@SubComponent注解
- 继承:extends关键字
看这篇文章的例子:[点击传送](http://www.jianshu.com/p/47c7306b2994)
总结
dagger2的工作原理
步骤1:查找Module中是否存在创建该类的方法。
- 步骤2:若存在创建类方法,查看该方法是否存在参数 步骤
- 若存在参数,则按从步骤1开始依次初始化每个参数
- 若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
- 步骤3:若不存在创建类方法,则查找Inject注解的构造函数, 看构造函数是否存在参数
- 若存在参数,则从步骤1开始依次初始化每个参数
- 若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
只要明白这里面的原则,基本上就是dagger2的基本使用用法了。