一什麼是依賴注入
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("");
}
}複製程式碼
這樣就完成了第三方庫構造方法有引數的注入
五 兩種注入方式,那麼他們有沒有優先順序呢?
當然是有的!他們的順序如下:
程式碼下載地址:github.com/XiFanYin/Da…