這篇文章主要記錄一下Dagger 2的使用,詳情Dagger 2 官方文檔
dagger 2 是一款依賴注入框架,通俗的就是一個類中的屬性對象(組合)通過框架注入而無需顯示調(diào)用 new Object ,主要的好處就是解耦,降低兩個類的關(guān)聯(lián).
1. @Inject
-
提供實例的類的是默認的構(gòu)造函數(shù)
- @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();
}
}
- @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 +
'}';
}
}
- 定義一個接口并用 @Component 標注
用來把 Engine 注入到 MainActivity 中
@Component
public interface MainComponent {
/**
* 必須讓Component知道需要往哪個類中注入,這個方法名可以是其它的,但是推薦用inject
* 目標類MainActivity必須精確,不能用它的父類
*/
void inject(MainActivity activity);
}
- 在 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ù)
- @Inject 標注的構(gòu)造函數(shù)提供
- @Module @Provides 提供
Dagger 優(yōu)先使用 @Module @Provides 提供的對象.
- 利用 @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 對象