Dagger2入门

一、为什么要使用Dagger2

Dagger2是一个Android端的依赖注入框架,如果你不了解什么是依赖注入的话,我想你看这篇文章也没有必要,当然如果是为了学习新技术,推荐去google一下什么叫依赖注入,有很多好文章,这篇介绍的是dagger2的使用,就不介绍什么是依赖注入了和依赖注入的好处了。

二、基本使用

使用时我们要有一个基本思想,所谓的依赖注入是事先把要用到的对象实例化,然后利用注入器注入到要用这个对象的地方。所以我们要做两件事,第一,实例化对象。第二,写一个构造器。

1、利用@Inject直接定义构造方法

现在有这样一个类

public class Animal {
    private String name;
    public Animal(){
        this.name ="puppy";
    }
}

我们要注入这样一个类,可以分一下几步走

1)利用@Inject注解构造方法,这一步就是直接宣告这个类可以通过这个构造方法实例化。

public class Animal {
    private String name;

    @Inject
    public Animal(){
        this.name ="puppy";
    }

    public String getName(){
        return name;
    }
}

2)利用@Component声明一个注入器

@Component()
public interface AnimalMainComponet {
    void inject( MainActivity mainActivity);
}
如果想要声明一个注入器,那么我们要创建一个接口,利用@Component声明,然后写一个inject方法,传入的必须是要注入的类的强类型,上面这个例子说明我们想注入到MainActivity中,注意,这里的方法可以随意命名,不会影响。当然,除了使用接口,我们也可以使用抽象类:

@Component()
public  abstract class AnimalMainComponet {
    abstract void inject( MainActivity mainActivity);
}

3)开始注入

因为dagger2是基于编译期生成代码的,所以先编译一下,就会自动生成一个以Dagger开头的类,利用这个类来注入

public class MainActivity extends AppCompatActivity {

    @Inject Animal animal;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView)findViewById(R.id.tv);
        DaggerAnimalMainComponet.builder().build().inject(this);
        tv.setText(animal.getName());

    }
}
利用@Inject直接声明下Animal,然后利用DaggerAnimalMainCompomet注入下就可以使用animal了。运行结果


利用@Inject直接注入构造方法非常简单,但是有缺点

public class User {
    private String name;
    private String age;



    public User(Car car){
        this.name =car.getName();
        this.age ="345";
    }

    public User(String name,String age){
        this.name =name;
        this.age = age;
    }

    public String getInfo(){
        return name+" "+age;
    }
}
这里有两个构造方法,但是,@Inject只能用来指明使用一个构造方法注入,而且这个方法还必须是无参的,在这里就不能这么用了。另外,我们经常用到第三方的包,是不可能修改第三方包中的构造方法的。所以这种注入方式虽然简单,但是不怎么用。我们接着介绍第二种方法:


2、利用@Module,@Provides注入。

还是以这个Animal为例,假设我么无法修改这个Animal类:

public class Animal {
    private String name;
    public Animal(){
        this.name ="puppy";
    }
}

我们按照以下步骤来

1)利用@Module声明一个类:这步是为了宣告这个类可以用来提供实例化好了的对象

@Module
public class AnimalModule {
    
}

2)在@Module类中利用@Provides来预先生成要用的对象。

@Module
public class AnimalModule {
    @Provides
    Animal provideAnimal(){
        return new Animal();
    }
}

这里真正提供Animal对象的是利用@Provides注解的provideAnimal方法,需要注意的是提供对象的方法必须以provide开头。

3)利用@Component声明注入器

@Component(modules = AnimalModule.class)
public  interface AnimalMainComponet {
    void inject( MainActivity mainActivity);
}

这里和上面不同的是要指明我们从哪里获取需要的对象,用的就是
@Component(modules = AnimalModule.class)
如果这里要从多个地方获取不同的对象,可以这样写 

@Component(modules = {AnimalModule.class,CarModule.class,UserModule.class})


4)开始注入

public class MainActivity extends AppCompatActivity {

    @Inject Animal animal;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView)findViewById(R.id.tv);
        DaggerAnimalMainComponet.builder().build().inject(this);
        tv.setText(animal.getName());

    }
}
真正注入的实现和第一种方法中提到的是一模一样的。

三、复杂应用

 1. 构造方法带参数怎么办

现在有这样一个类

public class Car {
    private String name;
    public Car(String name){
       this.name = name;
    }

    public String getName(){
        return name;
    }
}
用上面的第一种方法肯定不行,那让我们用第二种方法来写

写一个@Module,并且提供一个@Provides方法

@Module
public class CarModule {
    @Provides
    Car provideCar(){
        return new Car("宝马");
    }
}

这里问题来了,我们只能在这部把传入的参数写死了,但是我想在用的时候再传入参数怎么办呢?可以利用CarModule传入

@Module
public class CarModule {
    private String inputCarName;
    public CarModule(String name){
        inputCarName = name;
    }
    @Provides
    Car provideCar(){
        return new Car(inputCarName);
    }
}

这里我们在CarModule中定义了一个参数用来接收传入的参数,然后在provideCar中使用,在MainActivity中这样使用

public class MainActivity extends AppCompatActivity {

    @Inject Car car;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView)findViewById(R.id.tv);
        DaggerCarMainComponent.builder().carModule(new CarModule("奔驰")).build().inject(this);
        tv.setText(car.getName());

    }
}

和之前的比起来,注入的时候多了一个carModule方法,这里我们可以实例化自己的CarModule,随意传入参数。看下运行结果


2. 有多个构造方法怎么办

有这样一个类要注入:

public class User {
    private String name;
    private int age;
    public User(int age){
        this.age =age;
        name = "defaultName";
    }

    public User(String name){
        this.name =name;
        this.age =99;
    }

    public String getInfo(){
        return name+" "+age;
    }
}
这个类中有两个构造方法,那么我们在@Module中提供实例时也应该提供两个不同的方法,分别用不同的构造方法来生成对象,比如这样

 @Provides
    public User provideUserByName(){
        return new User(name);
    }

    @Provides
    public User provideUserByAge(){
        return new User(age);
    }

但是,很不幸,这样会报错,因为Dagger2框架不知道应该选哪个方法去生成User对象。知道原因就好解决了,我们可以用@Name注解表明下

@Module
public class UserModule {

    private String name;
    private  int age;
    public UserModule(String name,int age){
        this.name =name;
        this.age =age;
    }


    @Named("ByName")
    @Provides
    public User provideUserByName(){
        return new User(name);
    }

    @Named("ByAge")
    @Provides
    public User provideUserByAge(){
        return new User(age);
    }

}

在使用的时候可以这样用

public class MainActivity extends AppCompatActivity {

    @Named("ByName")
    @Inject
    User user1;

    @Named("ByAge")
    @Inject
    User user2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView)findViewById(R.id.tv);
        DaggerUserMainActivityComponent.builder().userModule(new UserModule("hello",100)).build().inject(this);
        tv.setText("ByName: "+user1.getInfo()+"  ByAge: "+user2.getInfo());

    }
}
这样就知道user1 是用了provideUserByName()这个方法生成,user2是用了provideUserByAge()这个方法生成的。这样问题确实解决了,但是字符串很容易出错啊,一不小心拼错了怎么办?  为了更方便的使用,我们可以使用@Qualifier 注解来自定义一个注解,达到上面的字符串一样的效果。

首先自定义两个注解来代替上面的两个字符串:

用来代替ByName字符串的注解@ByName

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ByName {
}
以及用来代替ByAge字符串的注解@ByAge

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ByAge {
}

用法这样的,在@Module中

    @ByName
    @Provides
    public User provideUserByName(){
        return new User(name);
    }

    @ByAge
    @Provides
    public User provideUserByAge(){
        return new User(age);
    }


在使用时

    @ByName
    @Inject
    User user1;

    @ByAge
    @Inject
    User user2;

3. 构造方法的参数中有要靠依赖注入的对象


先看下我们的项目结构,我们现在有三个类可以实现依赖注入了,现在我们有这样一个类

public class InfoUtil {
    public Animal animal;
    public Car car;
    public User user;

    public InfoUtil(Animal animal) {
        this.animal = animal;

    }

    public InfoUtil(Car car) {
        this.car = car;
    }

    public InfoUtil(User user) {
        this.user = user;
    }

    public String getAnimalInfo(){
        return "the name of animal is : "+animal.getName();
    }

    public String getCarInfo(){
        return "the name of car is :"+car.getName();
    }

    public String getUserInfo(){
        return ""+user.getInfo();
    }

}

我们来看看这个类的module怎么写

@Module
public class InfoUtilModule {


    @ByAnimal
    @Provides
    public InfoUtil provideInfoUtilByAnimal(Animal animal){
        return new InfoUtil(animal);
    }

    @ByUser
    @Provides
    public InfoUtil provideInfoUtilByUser(@ByName User user){
        return new InfoUtil(user);
    }

    @ByCar
    @Provides
    public InfoUtil provideInfoUtilByCar(Car car){
        return new InfoUtil(car);
    }
}

可以看到,这里定义了三个不同的注解来区分用不同的参数构造InfoUtil这个类,注意provideInfoUtilByUser这个方法,它的参数需要显示制定用那种方式注入,这里用的是@ByName。那么这些方法中的Animal ,User,Car从哪里来呢? 这里框架会自动从建立的依赖关系图中去获取,所以我们在写Component的时候就要指定提供这些对象的Module类,如下

@Component(modules = {InfoUtilModule.class, AnimalModule.class, CarModule.class, UserModule.class})
public interface InfoUtilComponent {
    public void inject(MainActivity mainActivity);
}

四、巨坑

同一个Activity只能在一个Component中注入,否则会报错,就是说如果在不同Component中的 inject方法中传入了同一个Activity的引用会报错。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值