Dagger 2 使用詳解-基礎(chǔ)篇

這篇文章主要記錄一下Dagger 2的使用,詳情Dagger 2 官方文檔

dagger 2 是一款依賴注入框架,通俗的就是一個類中的屬性對象(組合)通過框架注入而無需顯示調(diào)用 new Object ,主要的好處就是解耦,降低兩個類的關(guān)聯(lián).

1. @Inject

  • 提供實例的類的是默認的構(gòu)造函數(shù)
  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 注解在一個 構(gòu)造函數(shù)中,表示可以提供該類的實例供注入別的類中 ,有且只有一個Engine 的構(gòu)造函數(shù)能被 @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 方法中調(diào)用
// DaggerMainComponent 這個類是 Dagger 框架自動生成的輔助類,
DaggerMainComponent.create().inject(this);

之后就可以使用 mEngine 這個對象了,框架是通過調(diào)用 Engine 的構(gòu)造函數(shù)生成了一個 Engine 對象 并注入了 MainActivity

  • 注入的對象構(gòu)造函數(shù)有參數(shù),也需要提供參數(shù)對象
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 的構(gòu)造函數(shù)提供 Car 構(gòu)造函數(shù)的參數(shù)對象

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 構(gòu)造函數(shù)的 Tire 對象
    @Inject
    public Tire() {
        radius = 80;
    }

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

@Inject無法滿足所有的要求 :

  • 接口無法被構(gòu)造.
  • 引用的第三方框架對象無法被注解.
  • 可配置的對象無法被配置,只能使用構(gòu)造函數(shù)構(gòu)造

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 現(xiàn)在構(gòu)造函數(shù)有兩個地方可以提供 Engine Tire對象作為 Car 構(gòu)造函數(shù)的參數(shù)

  1. @Inject 標注的構(gòu)造函數(shù)提供
  2. @Module @Provides 提供

Dagger 優(yōu)先使用 @Module @Provides 提供的對象.

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

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

@Scope的作用主要是在組織Component和Module的時候起到一個實例作用范圍的提醒(類似是生命周期)

  • 已經(jīng)定義好的作用域 @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 注解結(jié)果為 false

@Singleton并不是我們傳統(tǒng)單例模式的那種作用,只能保證在一個Component當中只提供同一個實例對象 但并不是整個的應(yīng)用全局只有一個對象,調(diào)用DaggerMainComponent.create()兩次產(chǎn)生兩個 MainComponent對象也會產(chǎn)生兩個Car對象,要保證應(yīng)用全局單例可以配合android中application的特殊性,就可以實現(xiàn)應(yīng)用全局單例

Module中的方法 使用@Singleton標注后,那對應(yīng)的Component也必須采用@Singleton標注,表明它們的作用域一致,否則編譯的時候會報作用域不同的錯誤。

  • 自定義作用域

@ActivityScoped是一個自定義的作用域注解,作用是允許對象被記錄在正確的組件中,當然這些對象的生命周期應(yīng)該遵循activity的生命周期

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

@FragmentScoped是一個自定義的作用域注解,作用是允許對象被記錄在正確的組件中,當然這些對象的生命周期應(yīng)該遵循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();
    }

}

結(jié)果為 :

BenzCar

通過 @Binds 當需要注入一個 Car 對象 可以使用子類對象 BenzCar 進行綁定注入

6. @BindsInstance

上述注入對象都是通過 Dagger 自動注入的,如果我們需要 new 一個對象傳入 Dagger 就可以使用 @BindsInstance

public class Car {
    private Engine mEngine;
    private Tire mTire;
    // 提供一個 Car 對象,但是需要 Engine Tire對象作為參數(shù)
    @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();
    }
}



要構(gòu)造 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>對象的時候 只有在調(diào)用 get 方法的時候才會去調(diào)用 provideCar1 初始化 Car 對象,實現(xiàn)懶加載 且會緩存 Car 對象 每次進行 get()的時候返回的是同一個 Car 對象

  • Provider<T>

**把上述的代碼的 Lazy<Car> 改為 Provider<Car>,其他不變 **

car1 = car : false

當注入 Provider<Car> 對象的時候 每次調(diào)用 get 方法的時候才會去調(diào)用 provideCar1去創(chuàng)建一次新的 Car 對象

如果 Provider<Car> 注入過程中給 @Provides ,@Component 加上 @Singleton結(jié)果會如何呢 ?

@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();
}

結(jié)果為 :

car1 = car : true

當注入 Provider<Car> 對象的時候 每次調(diào)用 get 方法的時候才會去調(diào)用 provideCar1去創(chuàng)建 Car 對象,但是 provideCar1已經(jīng)被 @Singleton ,所以不管怎樣,provideCar1只會返回相同的Car 對象

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容