Dagger 2 使用详解-基础篇

这篇文章主要记录一下Dagger 2的使用,详情Dagger 2 官方文档

dagger 2 是一款依赖注入框架,通俗的就是一个类中的属性对象(组合)通过框架注入而无需显示调用 new Object ,主要的好处就是解耦,降低两个类的关联.

1. @Inject

  • 提供实例的类的是默认的构造函数
  1. @Inject 注解在一个 Filed 域. 表示在 MainActivity类中注入一个 Engine 对象
public class MainActivity extends AppCompatActivity {

    // 这个 @Inject 表示要注入一个 Engine对象
    @Inject Engine mEngine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainComponent.create().inject(this);

    }

    public void onClick(View view) {
        Toast.makeText(this,mEngine.toString(),Toast.LENGTH_SHORT).show();
    }
}

  1. @Inject 注解在一个 构造函数中,表示可以提供该类的实例供注入别的类中 ,有且只有一个Engine 的构造函数能被 @Inject 标注
public class Engine {
    private String model;
    private int age;

    // 提供 Engine 对象
    @Inject
    public Engine() {
        model = "xxxx";
        age = 1990;
    }
    
    public Engine(String model, int age) {
        this.model = model;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Engine{" +
                "model='" + model + '\'' +
                ", age=" + age +
                '}';
    }
}

  1. 定义一个接口并用 @Component 标注

用来把 Engine 注入到 MainActivity 中

@Component
public interface MainComponent {
    /**
     * 必须让Component知道需要往哪个类中注入,这个方法名可以是其它的,但是推荐用inject
     * 目标类MainActivity必须精确,不能用它的父类
     */
    void inject(MainActivity activity);
}
  1. 在 MainActivity 中 onCreate 方法中调用
// DaggerMainComponent 这个类是 Dagger 框架自动生成的辅助类,
DaggerMainComponent.create().inject(this);

之后就可以使用 mEngine 这个对象了,框架是通过调用 Engine 的构造函数生成了一个 Engine 对象 并注入了 MainActivity

  • 注入的对象构造函数有参数,也需要提供参数对象
public class MainActivity extends AppCompatActivity {

    @Inject Car mCar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainComponent.create().inject(this);

    }

    public void onClick(View view) {
        Toast.makeText(this,mCar.toString(),Toast.LENGTH_SHORT).show();
    }
}

dagger 框架会直接通过 Engine Tire的被 @Inject 的构造函数提供 Car 构造函数的参数对象

public class Car {
    private Engine mEngine;
    private Tire mTire;

    @Inject
    public Car(Engine engine,Tire tire) {
        mEngine = engine;
        mTire = tire;

    }

    @Override
    public String toString() {
        return "Car{" +
                "mEngine=" + mEngine +
                ", mTire='" + mTire + '\'' +
                '}';
    }
}

Engine对象见 1.2

public class Tire {


    private int radius;

    // 提供 Car 构造函数的 Tire 对象
    @Inject
    public Tire() {
        radius = 80;
    }

    @Override
    public String toString() {
        return "Tire{" +
                "radius=" + radius +
                '}';
    }
}

@Inject无法满足所有的要求 :

  • 接口无法被构造.
  • 引用的第三方框架对象无法被注解.
  • 可配置的对象无法被配置,只能使用构造函数构造

2. 使用 @Module @Provides

@Inject的局限性, Dagger 提供了另外一套提供实例的办法

@Module
public class TireModule {

    //可以提供一个 Tire 对象实例
    @Provides
    public static Tire provideTire()
    {
        return new Tire();
    }
}
@Module
public class EngineModule {

    // 可以提供一个 Engine 对象实例
    @Provides
    public static Engine provideEngine()
    {
       return new Engine("F1234",2018);
    }
}
@Component(modules = {TireModule.class,EngineModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}

当要注入 Car 现在构造函数有两个地方可以提供 Engine Tire对象作为 Car 构造函数的参数

  1. @Inject 标注的构造函数提供
  2. @Module @Provides 提供

Dagger 优先使用 @Module @Provides 提供的对象.

  1. 利用 @Module @Provides 也可以直接提供 Car 对象

3. @Scope:作用域 并不使用在类中,用于定义注解

@Scope的作用主要是在组织Component和Module的时候起到一个实例作用范围的提醒(类似是生命周期)

  • 已经定义好的作用域 @Singleton
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Provides 加上 @Singleton 表明会使用相同的 Car 对象去初始化所有需要 Car 对象的客户端


@Module
public class CarModule {
    @Provides
    @Singleton
    public static Car provideCar1()
    {
        return new Car(new Engine("F-7890",2020),new Tire());
    }
}

Component的也必须标注 @Singleton 不然会编译报错,作用域不匹配

@Component(modules = CarModule.class)
@Singleton
public interface MainComponent {
    Car maker();
}

public class MainActivity extends AppCompatActivity {
    private Car car1;
    private Car car2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MainComponent component = DaggerMainComponent.create();
        car1=component.maker();
        car2=component.maker();
    }

    public void onClick(View view) {
        Toast.makeText(this," car1==car2 : "+(car1==car2),Toast.LENGTH_SHORT).show();
    }
}

car1 = car : true

若去掉 @Singleton 注解结果为 false

@Singleton并不是我们传统单例模式的那种作用,只能保证在一个Component当中只提供同一个实例对象 但并不是整个的应用全局只有一个对象,调用DaggerMainComponent.create()两次产生两个 MainComponent对象也会产生两个Car对象,要保证应用全局单例可以配合android中application的特殊性,就可以实现应用全局单例

Module中的方法 使用@Singleton标注后,那对应的Component也必须采用@Singleton标注,表明它们的作用域一致,否则编译的时候会报作用域不同的错误。

  • 自定义作用域

@ActivityScoped是一个自定义的作用域注解,作用是允许对象被记录在正确的组件中,当然这些对象的生命周期应该遵循activity的生命周期

@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScoped {
}

@FragmentScoped是一个自定义的作用域注解,作用是允许对象被记录在正确的组件中,当然这些对象的生命周期应该遵循Fragment的生命周期


@Scope
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface FragmentScoped {}

4. @Qualifier:限定符 并不使用在类中,用于定义注解

  • 定义好的限定符 @Named
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}

    @Named("provideCar2")
    @Inject Car mCar;

在宿主中使用时,可以通过 @Named 来选择所需要的那个实例


@Module
public class CarModule {
    @Provides
    public static Car provideCar1()
    {
        return new Car(new Engine("F-7890",2020),new Tire());
    }
    @Provides
    @Named("provideCar2")
    public static Car provideCar2()
    {
        return new Car(new Engine("X-7890",2030),new Tire());
    }
}

@Component(modules = CarModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}
  • 自定义限定符
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ForTest {
}

5. @Binds

@Binds 可以用于当需要一个父类对象时,用子类对象进行替代 (接口同理)

public class Car {

}

public class BenzCar extends Car {

    @Inject
    public BenzCar() {
    }
}



@Module
public abstract class CarModule {
    
    @Binds
    abstract Car provideCar(BenzCar car);
}


@Component(modules = CarModule.class)
public interface MainComponent {
    Car maker();
}



public class MainActivity extends AppCompatActivity {
    @Inject
    Car car;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        car=DaggerMainComponent.create().maker();
    }

    public void onClick(View view) {
        Toast.makeText(this,car.getClass().getSimpleName(),Toast.LENGTH_SHORT).show();
    }

}

结果为 :

BenzCar

通过 @Binds 当需要注入一个 Car 对象 可以使用子类对象 BenzCar 进行绑定注入

6. @BindsInstance

上述注入对象都是通过 Dagger 自动注入的,如果我们需要 new 一个对象传入 Dagger 就可以使用 @BindsInstance

public class Car {
    private Engine mEngine;
    private Tire mTire;
    // 提供一个 Car 对象,但是需要 Engine Tire对象作为参数
    @Inject 
    public Car( Engine engine, Tire tire) {
        mEngine = engine;
        mTire = tire;

    }
    
}

public class Engine {
    private String model;
    private int age;
    public Engine(String model, int age) {
        this.model = model;
        this.age = age;
    }
}
public class Tire {


    private int radius;
    // Tire 不提供对象,手动注入
    public Tire(int radius) {
        this.radius = radius;
    }
}

@Module
public class EngineModule {

    // 提供 Engine 对象
    @Provides
    public static Engine provideEngine()
    {
       return new Engine("F1234",2018);
    }
}

@Component.Builder 注明一个 MainComponent builder @BindsInstance 定义一个传入手动对象的方法


@Component(modules = EngineModule.class)
public interface MainComponent {
    Car maker();

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder tire(Tire tire);
        MainComponent build();
    }
}



要构造 Car 对象需要 Engine Tire对象 ,Engine对象框架自动生成, Tire对象由我们手动传入


public class MainActivity extends AppCompatActivity {
    @Inject
    Car car;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 手动注入 Tire 对象
        car=DaggerMainComponent.builder().tire(new Tire(90)).build().maker();
    }

    public void onClick(View view) {
        Toast.makeText(this,car.toString(),Toast.LENGTH_SHORT).show();
    }

}

7. Lazy<T> 和 Provider<T>

  • Lazy<T>
@Module
public class CarModule {
    @Provides
    public static Car provideCar1()
    {
        return new Car(new Engine("F-7890",2020),new Tire());
    }

}

@Component(modules = CarModule.class)
public interface MainComponent {
    Lazy<Car> maker();
}

public class MainActivity extends AppCompatActivity {
    private Car car1;
    private Car car2;

    @Inject
    Lazy<Car> mCarLazy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCarLazy=DaggerMainComponent.create().maker();
        car1=mCarLazy.get();
        car2=mCarLazy.get();
    }

    public void onClick(View view) {
        Toast.makeText(this," car1==car2 : "+(car1==car2),Toast.LENGTH_SHORT).show();
    }

}

car1 = car : true

当注入 Lazy<Car>对象的时候 只有在调用 get 方法的时候才会去调用 provideCar1 初始化 Car 对象,实现懒加载 且会缓存 Car 对象 每次进行 get()的时候返回的是同一个 Car 对象

  • Provider<T>

**把上述的代码的 Lazy<Car> 改为 Provider<Car>,其他不变 **

car1 = car : false

当注入 Provider<Car> 对象的时候 每次调用 get 方法的时候才会去调用 provideCar1去创建一次新的 Car 对象

如果 Provider<Car> 注入过程中给 @Provides ,@Component 加上 @Singleton结果会如何呢 ?

@Module
public class CarModule {
    @Provides
    @Singleton
    public static Car provideCar1()
    {
        return new Car(new Engine("F-7890",2020),new Tire());
    }

}

@Component(modules = CarModule.class)
@Singleton
public interface MainComponent {
    Provider<Car> maker();
}

结果为 :

car1 = car : true

当注入 Provider<Car> 对象的时候 每次调用 get 方法的时候才会去调用 provideCar1去创建 Car 对象,但是 provideCar1已经被 @Singleton ,所以不管怎样,provideCar1只会返回相同的Car 对象

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容