Dagger2学习笔记(一)

最近刚接触到Dagger库,学习了Dagger1.xx和Dagger2.xx,前者是Square创建的,后者是Google fork前者开发的,这里记录一下学习过程。

首先是引入dagger依赖,编辑gradle脚本:
在project的gradle中,在buildscript->dependencies中添加:

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'

在app的gradle中,

apply plugin: 'android-apt'
dependencies {
    compile "com.google.dagger:dagger:2.0.2"
    apt 'com.google.dagger:dagger-compiler:2.0.2'
    provided 'org.glassfish:javax.annotation:10.0-b28'
}

如果有必要,还需要添加下面的代码(ps:这段代码是导入android annotations时使用的)

apt {
    arguments {
        // 新版本必须使用下面的
        //androidManifestFile variant.processResources.manifestFile
        androidManifestFile variant.outputs[0].processResources.manifestFile
        resourcePackageName 'this is your package name'
    }
}

点击菜单栏Tools->Android->Sync Projects with Gradle Files或者工具栏上一个向下箭头的图标执行构建脚本。

Dagger2里主要使用几个注解:

-@Inject
-@Module
-@Provides
-@Component
-@Scope
使用一段网上的解释:
@Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。

@Module: Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)。

@Provide: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。

@Component: Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。 Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如 果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。

@Scope: Dagger2可以通过自定义注解限定注解作用域。这是一个非常强大的特点,因为就如前面说的一样,没必要让每个对象都去了解如何管理他们的实例。我们用自定义的@PerActivity注解一个类,所以这个对象存活时间就和 activity的一样。简单来说就是我们可以定义所有范围的粒度(@PerFragment, @PerUser, 等等)。

还是使用官方文档的CoffeeMaker为例:
一个完整的流程为heater、pump->drink
1、定义3个接口

// Heater
public interface Heater {
    void on();
    void off();
    boolean isHot();
}
// Pump
public interface Pump {
    void pump();
    boolean isPumped();
}
// Drink
public interface Drink {
    void drink();
}

2、实现这3个接口

// ElectricHeater
public class ElectricHeater implements Heater {

    @Override
    public void on() {
        Log.i("test", "ElectricHeater on");
    }

    @Override
    public void off() {
        Log.i("test", "ElectricHeater off");
    }

    @Override
    public boolean isHot() {
        return false;
    }
}

// Thermosiphon 
public class Thermosiphon implements Pump {
    private final Heater heater;

    @Inject
    Thermosiphon(Heater heater) {
        this.heater = heater;
    }

    @Override
    public void pump() {
        Log.i("test", "Thermosiphon pump");
    }

    @Override
    public boolean isPumped() {
        return false;
    }
}

// PeopleDrink 
public class PeopleDrink implements Drink {
    private final Pump pump;

    @Inject
    PeopleDrink(Pump pump) {
        this.pump = pump;
    }

    @Override
    public void drink() {
        Log.i("test", "PeopleDrink drink");
    }

    public Pump getPump() {
        return this.pump;
    }
}

//CoffeeMaker 
public class CoffeeMaker {
// 第一种写法:
//    @Inject
//    Lazy<Heater> heater;
//    @Inject Pump pump;
//    @Inject Drink drink;
    /**
     * 对field进行Inject的,使用Inject的默认构造函数就可以了
     * 必须至少有一个Inject构造函数
     * 没有就使用默认的
     */
//    @Inject CoffeeMaker() {
//
//    }

// 第二种写法:
    /**
     * 使用下面这种写法,就必须在构造函数里设置属性
     * 构造函数必须是Inject的
     */
    private final Lazy<Heater> heater;
    private final Pump pump;
    private final Drink drink;


    @Inject
    CoffeeMaker(final Heater heater, Pump pump, Drink drink) {
        this.heater = new Lazy<Heater>() {
            @Override
            public Heater get() {
                return heater;
            }
        };
        this.pump = pump;
        this.drink = drink;
    }

    public void brew() {
        heater.get().on();
        pump.pump();
        heater.get().off();
        drink.drink();
    }

    public Pump getPump() {
        return pump;
    }
}

上面可以看到,在Thermosiphon 和PeopleDrink 的构造函数中使用的@Inject,同时相应的heater和pump声明为private final,当然final也可以不需要;ElectricHeater 中没有使用到@Inject,是因为不需要用到,使用默认构造函数就可以生成这个对象实例。
特别注意CoffeeMaker这个类,显示了两种构造方法,第一种方法中,在需要注入的成员前面直接添加@Inject,不过需要显示的创建默认构造函数,并且添加@Inject,否则编译会报错;第二种方法和前面的是一样的,就不解释了。

3、编辑module文件:

//DripCoffeeModule 
@Module
public class DripCoffeeModule {
    @Provides
    Heater provideHeater() {
        return new ElectricHeater();
    }

    @Singleton
    @Provides
    Pump providePump(Thermosiphon pump) {
        return pump;
    }

    @Provides
    Drink provideDrink(PeopleDrink drink) {
        return drink;
    }

}

首先使用@Module修饰整个类,里面的每个方法使用@Provides修饰,方法名一般使用provide前缀(这不是强制的),当需要生成对象实例时,就会根据这个module文件来创建。
如果有多个module文件需要联合使用,使用include依赖,像这样:

// DrinkModule 
@Module
public class DrinkModule {

    @Provides
    Drink provideDrink(PeopleDrink drink) {
        return drink;
    }
}

//DripCoffeeModule 
@Module(includes = DrinkModule.class)
public class DripCoffeeModule {
    @Provides
    Heater provideHeater() {
        return new ElectricHeater();
    }

    @Singleton
    @Provides
    Pump providePump(Thermosiphon pump) {
        return pump;
    }

}

这和上一段代码同样的效果

4、编辑component文件:
Within the interface, add methods for every object you need and it will automatically give you one with all its dependencies satisfied. In this case, I only need a Vehicle object, which is why there is only one method.
这个文件中,可以根据需求添加你想获得的对象,后面自动创建,这个例子中,我想得到CoffeeMaker和Pump两个对象,所以有两个方法:

@Singleton
@Component(modules = {DripCoffeeModule.class})
public interface CoffeeComponent {
    CoffeeMaker maker();
    Pump pump();
}

注意一下,@Component中的modules 的值,表明创建对象实例时,使用的是哪个module,这就是为什么module中提供那么多的@Provides方法的原因,如果需要多个module,可以使用逗号隔开。

5、创建Component实例,

CoffeeComponent coffeeShop = DaggerCoffeeComponent.builder()
                .dripCoffeeModule(new DripCoffeeModule())
                .build();

可以看到有DaggerCoffeeComponent这样一个类,这是我们前面写的代码编译后生成的,所以在编辑这一段代码之前,我们编译一下项目,就是工具栏上run左边的那个按钮,快捷键Ctrl+F9。
还可以写的简略一些:

CoffeeComponent coffeeShop = DaggerCoffeeComponent.create();

官方文档上是这么解释的:
Any module with an accessible default constructor can be elided as the builder will construct an instance automatically if none is set. If all dependencies can be constructed in that manner, the generated implementation will also have a create() method that can be used to get a new instance without having to deal with the builder.

现在就可以根据coffeeShop 实例来得到对象了,

CoffeeMaker maker = coffeeShop.maker().brew();
Pump pump = coffeeShop.pump()

前面在编辑module文件时,我在providePump的前面添加了@Singleton修饰,这表示在每个component中Pump实例只有一个,我们可以测试一下:

coffeeShop.maker().getPump() == coffeeShop.pump() // it's true

可见是返回的同一个对象。

补充一点在android中的应用, 假设这个Component在项目Application中创建,而在BaseActivity中需要注入对象,则需要这样:

// 在BaseActivity中
...
@Inject
Pump pump;
...

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.getApplicationComponent().inject(this);
  }
...
//返回你的Application中创建的component实例
protected ApplicationComponent getApplicationComponent() {
    return ((MyApplication)getApplication()).getCoffeeMakerComponent();
  }
  ...

注意到onCreate方法中的this.getApplicationComponent().inject(this);这句代码了吗,只有调用它,在可以正确注入对象,否则会报空指针错误。再看一下CoffeeComponent 中的改动:

...
public interface CoffeeComponent {
    void inject(BaseActivity activity);
    ...

添加了inject方法,这里要尤其注意inject的参数类型,必须和需要注入对象的类类型严格一致,例如此处需要在BaseActivity中注入,故参数类型是BaseActivity,如果你需要在XXXType类中注入,则需要在CoffeeComponent 中添加:

void inject(XXXTypea xxx);

然后在XXXType类中调用:

yoursComponent.inject(this);

后面会给出@Scope等其他注解的使用方法。

下面给出一些链接:
Dagger2
官方文档
When the Avengers meet Dagger2, RxJava and Retrofit in a clean way
Dependency Injection With Dagger 2 on Android

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值