Dagger2基礎篇(一)

稀飯_發表於2018-08-16

一什麼是依賴注入

Dagger2是依賴注入框架。很顯然,知道了什麼是依賴注入,就知道這個框架是為了解決什麼問題而生。

那麼什麼是依賴注入?

其實很簡單,這裡我先用程式碼舉個例子,然後我們再說定義:

比如一個人出門,他需要開車,那麼先去建立一個Person類,然後再去建立一個Car類程式碼如下:

public class Person {

    private Car car;

    public Person() {
        car = new Car();
    }

    public void chumen() {

        car.drive();
    }
}複製程式碼
public class Car {

    public Car() {

    }

    public void drive() {
        Log.e("rrrrrr", "開汽車出門");
    }
}複製程式碼

人需要藉助Car類完成出門這個需求,這種關係我們稱為Person類對Car類依賴。

那麼什麼是注入呢?

有一天,人需要乘坐火車出遠門,程式碼如下:

public class HotCar {

    public HotCar() {
    }

    public void drive() {
        Log.e("rrrrrr", "乘火車出遠門");
    }
}複製程式碼
public class Person {

//    private Car car;
    
    private HotCar hotCar;

    public Person() {
//        car = new Car();
        
        hotCar= new HotCar();
    }

    public void chumen() {

//        car.drive();
        hotCar.drive();
    }
}複製程式碼

人需要藉助HotCar類完成出門這個需求,Person類對HotCar依賴。你會發現,每次都要因為選擇不同的依賴工具而修改Person類,很繁瑣,於是我們想到把需要依賴的物件通過外部傳遞進來,修改程式碼如下:

public interface Drivors {
    
     void drive();
}
複製程式碼
public class Car implements Drivors {

    public Car() {

    }

    @Override
    public void drive() {
        Log.e("rrrrrr", "開汽車出門");
    }
}
複製程式碼
public class HotCar implements Drivors {

    public HotCar() {
    }
    @Override
    public void drive() {
        Log.e("rrrrrr", "乘火車出遠門");
    }
}
複製程式碼
public class Person {

    private Drivors drivors;

    public Person(Drivors drivors) {
        this.drivors = drivors;
    }

    public void chumen() {

        drivors.drive();
    }
}複製程式碼

使用上邊程式碼:

HotCar hotCar = new HotCar();
Person person = new Person(hotCar);
person.chumen();複製程式碼

你會發現不管換用什麼工具出門,我們不需要再修改Person類中的程式碼,Person所依賴的物件由外部傳入,這種效果我們叫依賴注入。

依賴注入的定義:內部不例項化依賴的物件,由外部傳入的寫法叫做依賴注入。

我們在日常開發中依賴注入的寫法有那些呢?

1通過構造方法注入(傳遞)

class A {

    B b;
    C c;

    public A(B b, C c) {
        this.b = b;
        this.c = c;
    }
}複製程式碼

2通過setter方法注入(傳遞)

class A {
    B b;
    C c;
    
    public void setB(B b) {
        this.b = b;
    }


    public void setC(C c) {
        this.c = c;
    }

}複製程式碼

通過上邊的簡單程式碼,我們直到了什麼是依賴注入,也知道了依賴注入的方式。

二Component 和Inject去注入物件

我們繼續回到開始:Dagger2是依賴注入。我們似乎明白了Dagger2乾的事情:就是把需求類內物件賦值的過程。

到此我們瞭解的Dagger2是為了解決什麼問題而誕生的。為依賴注入而誕生

接下來我們一步步去了解怎麼通過配置註解,然後完成最簡單的依賴注入。

首先我們要新增Dagger2依賴如下:

implementation 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'複製程式碼

然後我們去建立一個Car類

public class Car {
    @Inject
    public Car() {

    }
    public void drive() {
        Log.e("rrrrrr", "開汽車出門");
    }
}複製程式碼

然後給構造方法上新增一個@Inject註解,

對應的OneActivity程式碼如下:

public class OneActivity extends AppCompatActivity {

    @Inject
    Car car;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        car.drive();

    }
}複製程式碼

使用@Inject去註解這個需要注入的成員變數,下邊使用這個物件。執行發現他會報空指標異常。

這個時候我們要去建立一個介面把他們關聯起來,程式碼如下:

@Component
public interface A01Component {
    void inject(OneActivity activity);
}複製程式碼

使用@Component 註解一個介面,介面中定義一個傳入需要注入類的物件。然後再需要注入的類中寫下如下程式碼:

DaggerA01Component.builder().build().inject(this);複製程式碼

繼續執行,發現不報錯,並且可以呼叫drive方法,說明我們把物件注入成功。

上邊對應的Car構造方法沒有引數,但是如果Car有引數,我們怎麼才能注入呢?

這個時候我們就需要去建立一個model類對Dagger2提供這個引數。程式碼如下:

public class Car {
    private  String name;

    @Inject
    public Car(String name) {
        this.name = name;

    }

    public void drive() {
        Log.e("rrrrrr", name+"開汽車出門");
    }
}複製程式碼
@Module
public class A01Module {

    private String name;

    public A01Module(String name) {
        this.name = name;
    }

    @Provides
    public String provideName() {
        return name;
    }


}複製程式碼

然後把Module類和Component類進行關聯,如下:

@Component(modules = A01Module.class)
public interface A01Component {

    void inject(OneActivity activity);
}複製程式碼

最後使用如下:

public class OneActivity extends AppCompatActivity {

    @Inject
    Car car;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        DaggerA01Component.builder().a01Module(new A01Module("李四")).build().inject(this);

        car.drive();

    }
}複製程式碼

就完成有引數的類的注入。

三檢視Dagger2生成的原始碼

上邊我們通過一個介面就實現了賦值,既然是介面,就一定有實現類,關於實現類的生成涉及到另外一個技術APT技術(通過反射解析註解,生成程式碼的過程就叫做APT技術)。這裡我們不去研究怎麼生成的程式碼,而是去檢視原始碼裡邊的邏輯。

那麼原始碼怎麼找呢?

Project檢視下--build--source--apt--包名下就是Dagger2生成的程式碼:有一下三個檔案:

public enum Car_Factory implements Factory<Car> {
  INSTANCE;

  @Override
  public Car get() {
    return new Car();
  }

  public static Factory<Car> create() {
    return INSTANCE;
  }
}複製程式碼
public final class DaggerA01Component implements A01Component {
  private MembersInjector<OneActivity> oneActivityMembersInjector;

  private DaggerA01Component(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static A01Component create() {
    return builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.oneActivityMembersInjector = OneActivity_MembersInjector.create(Car_Factory.create());
  }

  @Override
  public void inject(OneActivity activity) {
    oneActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private Builder() {}

    public A01Component build() {
      return new DaggerA01Component(this);
    }
  }
}複製程式碼
public final class OneActivity_MembersInjector implements MembersInjector<OneActivity> {
  private final Provider<Car> carProvider;

  public OneActivity_MembersInjector(Provider<Car> carProvider) {
    assert carProvider != null;
    this.carProvider = carProvider;
  }

  public static MembersInjector<OneActivity> create(Provider<Car> carProvider) {
    return new OneActivity_MembersInjector(carProvider);
  }

  @Override
  public void injectMembers(OneActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.car = carProvider.get();
  }

  public static void injectCar(OneActivity instance, Provider<Car> carProvider) {
    instance.car = carProvider.get();
  }
}複製程式碼

通過上邊原始碼可以看出,把建立的Car物件和傳入的OneActivity物件一起傳入OneActivity_MembersInjector類中,然後在injectMembers方法中完成的賦值。

上邊是一個通過Dagger2實現依賴注入的最簡單例子。

四第三方庫怎麼用Dagger2注入

通過上邊的兩個例子我們發現,@Component 和 @Inject 的配合就能夠使用 Dagger2 了,但這裡面存在一個侷限,@Inject 只能標記在我們自己編寫的類的構造方法中,如果我們使用第三方的庫或者標準庫的話,我們就沒有辦法用這兩個註解完成依賴注入了,這時候,我們就需要新的註解@Provides 和 @Module

接下來的需求大概是這樣,我們假如Person類是第三方庫中的類,不讓Person類中出現任何註解,然後使用Dagger去注入

public class Pseson {

    public Pseson() {
    }

    public String getName() {
        return "張三";
    }
}複製程式碼

然後建立Component

@Component
public interface A02Component {

    void inject(TwoActivity activity);
}複製程式碼

最後使用:

public class TwoActivity extends AppCompatActivity {

    @Inject
    Pseson person;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        DaggerA02Component.builder().build().inject(this);
        person.getName();

    }
}複製程式碼

這個時候編譯發現失敗,是因為Person構造沒有新增註解。這個時候我們就需要去建立一個Model類

@Module
public class A02Module {

    @Provides
    public Pseson providePerson() {
        return new Pseson();
    }
}複製程式碼

值得注意的地方有
- @Provides 修飾的方法一般用 provide 作用方法名字首。
- @Module 修飾的類一般用 Module 作為字尾。

在 @component 註解後面的括號中取值module.class。

@Component(modules = A02Module.class)
public interface A02Component {

    void inject(TwoActivity activity);
}複製程式碼

這樣我們就通過這個四個註解完成了第三方類的物件注入。

如果第三方庫中構造方法也有引數,這個我們怎麼注入呢?假如上邊的Person依賴Car類。

我們就需要修改我們module中的程式碼,也去提供這個引數即可。修改後的module如下:

@Module
public class A02Module {

    @Provides
    public Pseson providePerson(Car car) {
        return new Pseson(car);
    }

    @Provides
    public Car provideCar() {

        return new Car("");
    }
}複製程式碼

這樣就完成了第三方庫構造方法有引數的注入

五 兩種注入方式,那麼他們有沒有優先順序呢?

當然是有的!他們的順序如下:

  • 步驟1:查詢Module中是否存在建立該類的方法。
  • 步驟2:若存在建立類方法,檢視該方法是否存在引數
  • 步驟2.1:若存在引數,則按從步驟1開始依次初始化每個引數
  • 步驟2.2:若不存在引數,則直接初始化該類例項,一次依賴注入到此結束
  • 步驟3:若不存在建立類方法,則查詢Inject註解的建構函式,看建構函式是否存在引數
  • 步驟3.1:若存在引數,則從步驟1開始依次初始化每個引數
  • 步驟3.2:若不存在引數,則直接初始化該類例項,一次依賴注入到此結束

  • 通過上邊的學習我們學會了兩種注入方式的基本使用,以及他們之間的優先順序和如果有引數情況下怎麼提供引數。我相信通過這篇文章的閱讀,你的Dagger2一定能達到入門級別,知道這個東西是什麼,簡單怎麼使用。後續還會有兩篇文章更新,第二篇講解一些高階的用法。第三篇講解怎樣把Dagger2融合到mvp專案中去。

    程式碼下載地址:github.com/XiFanYin/Da…




    相關文章