什么是Dagger2 ?
Dagger is a fully static, compile-time dependency injection framework for
both Java and Android. It is an adaptation of an earlier version
created bySquare and now maintained by Google.Dagger aims to address many of the development and performance issues that have
plagued reflection-based solutions.
dagger2是一個基于JSR-330標準的依賴注入框架,在編譯期間自動生成代碼,負責依賴對象的創建。butterknife也是一個依賴注入框架。不過butterknife黃油刀,Dagger2叫做利器,他的主要作用,就是對象的管理,其目的是為了降低程序耦合。
Java 依賴注入標準 JSR-330 簡介概念
JSR-330 是 Java 的依賴注入標準。定義了如下的術語描述依賴注入:
- A 類型依賴 B類型(或者說 B 被 A 依賴),則 A類型 稱為”依賴(物) dependency”
- 運行時查找依賴的過程,稱為”解析 resolving“依賴
- 如果找不到依賴的實例,稱該依賴是”不能滿足的 unsatisfied”
- 在”依賴注入 dependency injection”機制中,提供依賴的工具稱為”依賴注入器 dependency injector”
依賴注入
我們知道Dagger是一個依賴注入的框架,那么什么是依賴注入呢?
我們在activity中有可能會用到很多很多的類,這些類要在activity中進行實例化,這樣就導致我們的activity非常依賴這么多的類,這樣的程序耦合非常
嚴重,不便于維護和擴展,有什么辦法可以不去依賴這些類呢,這時候就需要有一個容器(IoC),將這些類放到這個容器里并實例化,我們activity在用
到的時候去容器里面取就可以了,我們從依賴類到依賴這個容器,實現了解耦,這就是我所理解的依賴注入,即所謂控制反轉;
控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫依賴查找(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。
提示:本篇文章內容代碼改動次數較多,所以為了保留原代碼,分次上傳只需進入源碼地址clone下來,并進入不同的commit即可查看相關正確代碼。
Dagger接入項目
簡單的說 Dagger就是用來創造這個容器,所有需要被依賴的對象在Dagger的容器中實例化,并通過Dagger注入到合適的地方,實現解耦,MVP框架就是為解耦而生,因此MVP和Dagger是絕配;
從簡單示例入門
(1) app.gradle引入依賴庫
dependencies {
implementation 'com.google.dagger:dagger:2.20'
annotationProcessor 'com.google.dagger:dagger-compiler:2.20'
}
(2)總結幾個很重要的“單詞”
@Inject
通常在需要依賴的地方使用這個注解。換句話說,你用它告訴Dagger這個類或者字段需要依賴注入。這樣,Dagger就會構造一個這個類的實例并滿足他們的依賴。 官方點說就是帶有此注解的屬性或構造方法將參與到依賴注入中,Dagger2會實例化有此注解的類。
@Module
Modules類里面的方法專門用來提供依賴,他就像一個工廠一樣去生產需要添加依賴的實例。所以我們定義一個類,用@Module來注解,這樣Dagger在構造類的實例的時候,就知道從哪里去找到需要的依賴。modules的一個重要特性是它們設計為分區并組合在一起(例如,我們的app中可以有多個組成在一起的modules)。它里面定義一些用@Provides注解的以provide開頭的方法,這些方法就是所提供的依賴,Dagger2會在該類中尋找實例化某個類所需要的依賴。
@Provides
上面引入了這個概念了。在modules中,我們定義的方法是用@Provides這個注解,以此來告訴Dagger我們想要構造對象并提供這些依賴。
@Component
Components從根本上來說他就是一個注入器,也可以說是用來將@Inject和@Module聯系起來的橋梁,它的主要作用就是連接這兩個部分。Components可以提供所有定義了的類型的實例(inject需要),比如:我們必須用@Component注解一個接口然后列出所有的 。功能是從@Module中獲取依賴并將依賴注入給@Inject
(3)實戰操練
public class Rose {
public String whisper() {
return "My lover";
}
}
public class Pot {
private Rose mRose;
public Pot(Rose mRose) {
this.mRose = mRose;
}
public String show() {
return mRose.whisper();
}
}
public class DaggerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
toastWhisper();
}
private void toastWhisper() {
Rose rose = new Rose();
Pot pot = new Pot(rose);
Toast.makeText(this, pot.show(), Toast.LENGTH_SHORT).show();
}
}
使用Dagger2進行依賴注入如下:
public class Rose {
@Inject
public Rose() {
}
public String whisper() {
return "My lover";
}
}
public class Pot {
private Rose mRose;
@Inject
public Pot(Rose mRose) {
this.mRose = mRose;
}
public String show() {
return mRose.whisper();
}
}
public class DaggerActivity extends AppCompatActivity {
@Inject
Pot pot;// injected field can not be private
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
// 這個類是重新編譯后Dagger2自動生成的,所以寫這行代碼之前要先編譯一次 Build --> Rebuild Project
DaggerDaggerActivityComp.create().inject(this);
toastWhisper();
}
private void toastWhisper() {
Toast.makeText(this, pot.show(), Toast.LENGTH_SHORT).show();
}
}
這樣我們就可以讓目標類DaggerActivity中所依賴的其他類,與其他類的構造函數之間有了一種無形的映射聯系。 這個時候Dagger2就會去找這個類的實例,文章開頭也說了Components是一個橋梁,Dagger2會到中介Components中去找這個實例的(其實是借助Compoent找,他類似中介)。
所以要創建一個Component,概念也說了:我們必須用@Component注解一個接口然后列出所有的 ,他提供的是一系列的接口。
現在了解下@Inject
和@Component
兩個API,想要使用Dagger2進行依賴注入,至少要使用到這兩個注解。
@Inject
用于標記需要注入的依賴,或者標記用于提供依賴的方法。
@Component
則可以理解為注入器,在注入依賴的目標類MainActivity
使用Component完成注入。
@Inject
依賴注入中第一個并且是最重要的就是@Inject
注解,標記那些應該被依賴注入框架提供的依賴。在Dagger 2中有3種不同的方式來提供依賴:
構造器注入,@Inject標注在構造器上其實有兩層意思。
①告訴Dagger2可以使用這個構造器構建對象。如Rose
類
②注入構造器所需要的參數的依賴。 如Pot
類,構造上的Rose會被注入。
構造器注入的局限:如果有多個構造器,我們只能標注其中一個,無法標注多個。屬性注入
如DaggerActivity
類,標注在屬性上。被標注的屬性不能使用private
修飾,否則無法注入。
屬性注入也是Dagger2中使用最多的一個注入方式。方法注入
標注在public方法上,Dagger2會在構造器執行之后立即調用這個方法。
方法注入和屬性注入基本上沒有區別, 那么什么時候應該使用方法注入呢?
比如該依賴需要this對象的時候,使用方法注入可以提供安全的this對象,因為方法注入是在構造器之后執行的。
/**
* Method injection is used here to safely reference {@code this} after the object is created.
* For more information, see Java Concurrency in Practice.
*/
@Inject
void setupListeners() {
mTasksView.setPresenter(this);
}
@Component
@Inject
注解只是JSR-330中定義的注解,這個注解本身并沒有作用,它需要依賴于注入框架才具有意義,用來標記需要被注入框架注入的方法,屬性,構造。
而Dagger2則是用Component
來完成依賴注入的,可以理解為注入器,@Component
可以說是Dagger2中最重要的一個注解。
@Component
public interface DaggerActivityComp {
void inject(DaggerActivity daggerActivity);
}
命名方式推薦為:目標類名+Component
,在編譯后Dagger2就會為我們生成DaggerXXXComponent
這個類,它是我們定義的xxxComponent
的實現,在目標類中使用它就可以實現依賴注入了。
Component中一般使用兩種方式定義方法。
void inject(目標類 obj);
Dagger2會從目標類開始查找@Inject注解,自動生成依賴注入的代碼,調用inject可完成依賴的注入。Object getObj();
如:Pot getPot();
Dagger2會到Pot類中找被@Inject注解標注的構造器,自動生成提供Pot依賴的代碼,這種方式一般為其他Component提供依賴。(一個Component可以依賴另一個Component)
Dagger2框架以Component中定義的方法作為入口,到目標類中尋找@Inject標注,生成一系列提供依賴的Factory類和注入依賴的Injector類。
而Component則是聯系Factory和Injector,最終完成依賴的注入。
@Module和@Provides
使用@Inject標記構造器提供依賴是有局限性的,比如說我們需要注入的對象是第三方庫提供的,我們無法在第三方庫的構造器上加上@Inject注解。
或者,我們使用依賴倒置的時候,因為需要注入的對象是抽象的,@Inject也無法使用,因為抽象的類并不能實例化,比如我們這里使用如下兩個類OkHttpClient和RetrofitManager設定不可改動代碼的情況:
public class OkHttpClient {
private int timeout;
public OkHttpClient() {
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
}
@Module
public class DaggeActivityModule {
@Provides
OkHttpClient provideOkHttpClient() {
return new OkHttpClient();
}
}
private void toastWhisper() {
Toast.makeText(this, pot.show(), Toast.LENGTH_SHORT).show();
Toast.makeText(this, okHttpClient.toString(), Toast.LENGTH_SHORT).show();
}
編寫步驟:
第一步,編寫Module類并使用@Module標注這個類,編寫方法返回值為我們需要inject的類型并使用@Provides標注這個方法;
第二步,編寫Component接口,使用@Component標注這個接口,并使用modules=的方法鏈接上第一步中編寫的Module類;
之后的步驟就和 1 中的inject一樣了。
@Module需要和@Provide是需要一起使用的時候才具有作用的,并且@Component也需要指定了該Module的時候。
@Module是告訴Component,可以從這里獲取依賴對象。Component就會去找被@Provide標注的方法,相當于構造器的@Inject,可以提供依賴。
還有一點要說的是,@Component可以指定多個@Module的,如果需要提供多個依賴的話。并且Component也可以依賴其它Component存在。
復雜Module使用:
1、如果我們希望在使用的時候才傳入一些配置,直接使用Module的構造參數傳入即可,這種用法注意DaggerActivity中Component實例化的時候使用builder模式傳入了我們需要傳入的值;
@Module
public class DaggeActivityModule {
private int timeout;
public DaggeActivityModule(int timeout) {
this.timeout = timeout;
}
@Provides
OkHttpClient provideOkHttpClient() {
OkHttpClient client = new OkHttpClient();
client.setTimeout(timeout);
return client;
}
@Provides
RetrofitManager provideretrofitManager(OkHttpClient client) {
return new RetrofitManager(client);
}
}
public class DaggerActivity extends AppCompatActivity {
private static final String TAG = "DaggerActivity";
@Inject
Pot pot;
@Inject
Rose rose;
@Inject
Flower flower;
@Inject
OkHttpClient okHttpClient;
@Inject
RetrofitManager retrofitManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
// 這個類是重新編譯后Dagger2自動生成的,所以寫這行代碼之前要先編譯一次 Build --> Rebuild Project
DaggerDaggerActivityComp.builder().daggeActivityModule(new DaggeActivityModule(3000)).flowerModule(new FlowerModule()).build().inject(this);
toastWhisper();
}
private void toastWhisper() {
Log.d(TAG, " pot.show() is :" + pot.show());
Log.d(TAG, " rose is :" + rose.whisper());
Log.d(TAG, " flower is :" + flower.toString());
Log.d(TAG, " okHttpClient.toString() is :" + okHttpClient.toString());
Log.d(TAG, " retrofitManager.getOkHttpClient().toString() is :" + retrofitManager.getOkHttpClient().toString());
}
}
可以看到,被@Module注解的類生成的也是Factory。
a、在Module的構造函數帶有參數且參數被使用的情況下,所生產的Component類就沒有create()方法了。
b、熟練使用1和2,我們就能配合mvp進行使用dagger2了,比如將presenter注入到view層;值得一提的是谷歌不推薦直接將presenter的構造參數添加注解,更加推薦的是將presenter放到Module里進行管理,因為這樣代碼更加容易管理。
@Module需要和@Provide是需要一起使用的時候才具有作用的,并且@Component也需要指定了該Module的時候。
@Module是告訴Component,可以從這里獲取依賴對象。Component就會去找被@Provide標注的方法,相當于構造器的@Inject,可以提供依賴。
還有一點要說的是,@Component可以指定多個@Module的,如果需要提供多個依賴的話。
如下,將代碼稍微修改:
注意此錯誤
dagger2XXX cannot be provided without an @Inject constructor or from an @Provides-annotated
public abstract class Flower {
abstract String whisper();
}
public class Lily extends Flower {
@Override
public String whisper() {
return "My lover Lily";
}
}
public class Rose extends Flower {
@Inject
public Rose() {
}
@Override
public String whisper() {
return "My lover Rose";
}
}
@Module
public class FlowerModule {
@Provides
Flower provideRose() {
return new Rose();
}
@Provides
Pot providePot() {
return new Pot(new Lily());
}
}
@Component(modules = {DaggeActivityModule.class,FlowerModule.class})
public interface DaggerActivityComp {
void inject(DaggerActivity daggerActivity);
}//打印如下
/ming.com.andcode D/DaggerActivity: pot.show() is :My lover Lily
/ming.com.andcode D/DaggerActivity: rose is :My lover Rose
/ming.com.andcode D/DaggerActivity: flower is :ming.com.andcode.dagger2.delo.Rose@1ec13658
/ming.com.andcode D/DaggerActivity: okHttpClient.toString() is :ming.com.andcode.dagger2.delo.OkHttpClient@16e323b1
/ming.com.andcode D/DaggerActivity: retrofitManager.getOkHttpClient().toString() is :ming.com.andcode.dagger2.delo.OkHttpClient@3f7c5096
@Qualifier和@Named
@Qualifier是限定符,而@Named則是基于String的限定符。
當我有兩個相同的依賴(都繼承某一個父類或者都是先某一個接口)可以提供給高層時,那么程序就不知道我們到底要提供哪一個依賴,因為它找到了兩個。
這時候我們就可以通過限定符為兩個依賴分別打上標記,指定提供某個依賴。
我們修改以下代碼,使FlowerModule提供兩個Flower與一個Pot
@Module
public class FlowerModule {
@Provides
Flower provideRose() {
return new Rose();
}
@Provides
Flower provideLily() {
return new Lily();
}
@Provides
Pot providePot() {
return new Pot(new Lily());
}
}
編譯報錯:
錯誤: [Dagger/DuplicateBindings] ming.com.andcode.dagger2.delo.Flower is bound multiple times:
public interface DaggerActivityComp {
^
@Provides ming.com.andcode.dagger2.delo.Flower ming.com.andcode.dagger2.module.FlowerModule.provideLily()
@Provides ming.com.andcode.dagger2.delo.Flower ming.com.andcode.dagger2.module.FlowerModule.provideRose()
ming.com.andcode.dagger2.delo.Flower is injected at
ming.com.andcode.dagger2.DaggerActivity.flower
ming.com.andcode.dagger2.DaggerActivity is injected at
ming.com.andcode.dagger2.component.DaggerActivityComp.inject(ming.com.andcode.dagger2.DaggerActivity)
@Qualifier的作用和@Named是完全一樣的,不過更推薦使用@Qualifier,因為@Named需要手寫字符串,容易出錯。@Qualifier不是直接注解在屬性上的,而是用來自定義注解的。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface LilyFlower {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface RoseFlower {
}
@Module
public class FlowerModule {
@Provides
@RoseFlower
Flower provideRose() {
return new Rose();
}
@Provides
@LilyFlower
Flower provideLily() {
return new Lily();
}
}
public class Pot {
private Flower flower;
@Inject
public Pot(@RoseFlower Flower mRose) {
this.flower = mRose;
}
public String show() {
return flower.whisper();
}
}
Component依賴Component
當我們其中一個Component跟另外一個Component所提供的依賴有重復的時候,我們沒有必要完全再寫一遍,一個Component是可以依賴另外一個依賴的,理解起來就像extends關鍵字;
有兩種實現方式:dependence和@SubComponent
Component dependencies - Use this when you want to keep two components independent.
Subcomponents - Use this when you want to keep two components coupled.
a、dependence方式
PotModule需要依賴LilyFlower,需要指定其中一個子類實現,這里使用LilyFlower
@Module
public class PotModule {
@Provides
Pot providePot(@LilyFlower Flower flower) {
return new Pot(flower);
}
}
@Component(modules = FlowerModule.class)
public interface FlowerComponent {
@RoseFlower
Flower getRoseFlower();
@LilyFlower
Flower getLilyFlower();
}
@Component(modules = PotModule.class, dependencies = FlowerComponent.class)
public interface PotComponent {
Pot getPot();
}
@Component(modules = {DaggeActivityModule.class},dependencies = PotComponent.class)
public interface DaggerActivityComp {
void inject(DaggerActivity daggerActivity);
}
而在MainActivity則需要創建其依賴的Component
public class DaggerActivity extends AppCompatActivity {
private static final String TAG = "DaggerActivity";
@Inject
Rose rose;
@Inject
Pot pot;
@Inject
OkHttpClient okHttpClient;
@Inject
RetrofitManager retrofitManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
// 這個類是重新編譯后Dagger2自動生成的,所以寫這行代碼之前要先編譯一次 Build --> Rebuild Project
DaggerDaggerActivityComp.builder().potComponent(DaggerPotComponent.builder().flowerComponent(DaggerFlowerComponent.create()).build()).
daggeActivityModule(new DaggeActivityModule(3000)).build().inject(this);//創建其依賴的Component
toastWhisper();
}
private void toastWhisper() {
Log.d(TAG, " rose is :" + rose.whisper());
Log.d(TAG, " pot is :" + pot.show());
Log.d(TAG, " okHttpClient.toString() is :" + okHttpClient.toString());
Log.d(TAG, " retrofitManager.getOkHttpClient().toString() is :" + retrofitManager.getOkHttpClient().toString());
}
}
打印結果如下
ming.com.andcode D/DaggerActivity: rose is :My lover Rose
ming.com.andcode D/DaggerActivity: pot is :My lover Lily
ming.com.andcode D/DaggerActivity: okHttpClient.toString() is :ming.com.andcode.dagger2.delo.OkHttpClient@1ec13658
ming.com.andcode D/DaggerActivity: retrofitManager.getOkHttpClient().toString() is :ming.com.andcode.dagger2.delo.OkHttpClient@16e323b1
dependence實現方式總結:
1、父Component中要顯式的寫出需要暴露可提供給子Component的依賴;
2、子Component在注解中使用dependencies=來連接父Component;
3、注意子Component實例化方式。
b、Subcomponent方式
@Component(modules = FlowerModule.class)
public interface FlowerComponent {
PotComponent plus(PotModule potModule);
}
@Subcomponent(modules = PotModule.class)
public interface PotComponent {
DaggerActivityComp plus();
}
@Subcomponent
public interface DaggerActivityComp {
void inject(DaggerActivity daggerActivity);
}
public class DaggerActivity extends AppCompatActivity {
private static final String TAG = "DaggerActivity";
@Inject
Pot pot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
// 這個類是重新編譯后Dagger2自動生成的,所以寫這行代碼之前要先編譯一次 Build --> Rebuild Project
DaggerFlowerComponent.create().
plus(new PotModule())// 這個方法返回PotComponent
.plus() // 這個方法返回MainActivityComponent
.inject(this);
toastWhisper();
}
private void toastWhisper() {
Log.d(TAG, " pot is :" + pot.show());
}
}
打印結果:
/ming.com.andcode D/DaggerActivity: pot is :My lover Lily
subComponent實現方式總結:
1、在子Component,定義一個接口或抽象類(通常定義為xxBuilder),使用@Subcomponent.Builder標注:
? (一)、編寫返回值為xxBuilder,方法的參數為需要傳入參數的Module,(二)、編寫返回值為當前子Component的無參方法;
2、父Component中定義獲得子Component.Builder的方法;
FlowerComponent管理了PotComponent和DaggerActivityComp,看起來不符合常理。
但是用Subcomponent怎么看怎么別扭,各個Component之間聯系太緊密,不太適合我們Demo的使用場景。
那什么時候該用@Subcomponent呢?
Subcomponent是作為Component的拓展的時候。
像我寫的Demo中,Pot和Flower還有MainActivity只是單純的依賴關系。就算有,也只能是Flower作為Pot的Subcomponent,而不是Demo中所示,因為我需要給大家展示Dagger的API,強行使用。
比較適合使用Subcomponent的幾個場景:
很多工具類都需要使用到Application的Context對象,此時就可以用一個Component負責提供,我們可以命名為AppComponent。
需要用到的context對象的SharePreferenceComponent,ToastComponent就可以它作為Subcomponent存在了。
而且在AppComponent中,我們可以很清晰的看到有哪些子Component,因為在里面我們定義了很多XxxComponent plus(Module... modules)
每個ActivityComponent也是可以作為AppComponent的Subcomponent,這樣可以更方便的進行依賴注入,減少重復代碼。
Component dependencies和Subcomponent使用上的總結
Dependencies:
1,你想保留獨立的想個組件(Flower可以單獨使用注入,Pot也可以)
2,要明確的顯示該組件所使用的其他依賴
Subcomponent:
1,兩個組件之間的關系緊密
2,你只關心Component,而Subcomponent只是作為Component的拓展,可以通過Component.xxx調用。
@Scope和@Singleton
@Scope是用來管理依賴的生命周期的。它和@Qualifier一樣是用來自定義注解的,而@Singleton則是@Scope的默認實現。
public class DaggerActivity extends AppCompatActivity {
private static final String TAG = "DaggerActivity";
@Inject
Pot pot;
@Inject
Pot poti;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
// 這個類是重新編譯后Dagger2自動生成的,所以寫這行代碼之前要先編譯一次 Build --> Rebuild Project
DaggerDaggerActivityComp.builder().potComponent(DaggerPotComponent.builder().flowerComponent(DaggerFlowerComponent.create()).build()).
daggeActivityModule(new DaggeActivityModule(3000)).build().inject(this);
toastWhisper();
}
private void toastWhisper() {
Log.d(TAG, " pot is :" + pot.show()+" pot hashcode is "+pot.hashCode());
Log.d(TAG, " poti is :" + poti.show()+" poti hashcode is "+poti.hashCode());
}
}
打印結果:
ming.com.andcode D/DaggerActivity: pot is :My lover Lily pot hashcode is 515978840
/ming.com.andcode D/DaggerActivity: poti is :My lover Lily poti hashcode is 383984561
假設我們需要Pot對象的生命周期和app相同,也就是單例,我們需要怎么做?這時候就可以用到@Scope注解了。
我們來使用默認的@Scope實現——@Singleton
a、無Module的使用方式,只需提供依賴的類及Component都添加@Singleton標注即可;
b、如果是帶Module的,Component必須添加@Singleton標注,然后再根據需要給Module中@provides標注的方法再標注上@Singleton
@Module
public class PotModule {
@Provides
@Singleton
Pot providePot(@LilyFlower Flower flower) {
return new Pot(flower);
}
}
@Singleton
@Component(modules = PotModule.class, dependencies = FlowerComponent.class)
public interface PotComponent {
Pot getPot();
}
錯誤: ming.com.andcode.dagger2.component.DaggerActivityComp (unscoped) cannot depend on scoped components:
@Component(modules = {DaggeActivityModule.class},dependencies = PotComponent.class)
^
@Singleton ming.com.andcode.dagger2.component.PotComponent
那么,我們在PotComponent子類DaggerActivityComp添加@Singleton
呢?
1,PotComponent無@Singleton
,DaggerActivityComp添加@Singleton
錯誤: [Dagger/IncompatiblyScopedBindings] ming.com.andcode.dagger2.component.PotComponent (unscoped) may not reference scoped bindings:
public interface PotComponent {
^
@Provides @Singleton ming.com.andcode.dagger2.delo.Pot ming.com.andcode.dagger2.module.PotModule.providePot(@ming.com.andcode.dagger2.qualifier.LilyFlower ming.com.andcode.dagger2.delo.Flower)
2,PotComponent有@Singleton
,DaggerActivityComp有@Singleton
錯誤: This @Singleton component cannot depend on scoped components:
@Component(modules = {DaggeActivityModule.class},dependencies = PotComponent.class)
^
@Singleton ming.com.andcode.dagger2.component.PotComponent
那是因為我們的DaggerActivityComp依賴PotComponent,而dagger2規定子Component也必須標注@Scope。
但是我們不能給DaggerActivityComp也標注@Singleton,并且dagger2也不允許。因為單例依賴單例是不符合設計原則的,我們需要自定義一個@Scope注解。
定義Scope是名字要起得有意義,能一眼就讓你看出這個Scope所規定的生命周期。
比如ActivityScope 或者PerActivityScope,生命周期和Activity相同。
/**
* Identifies a type that the injector only instantiates once. Not inherited.
*
* @see javax.inject.Scope @Scope
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
仿照Singleton,我們自己定義Scope
/**
* Identifies a type that the injector only instantiates once. Not inherited.
*
* @see javax.inject.Scope @Scope
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {}
將自定義Scope引入
@ActivityScope
@Component(modules = {DaggeActivityModule.class},dependencies = PotComponent.class)
public interface DaggerActivityComp {
void inject(DaggerActivity daggerActivity);
}
private void toastWhisper() {
Log.d(TAG, " pot is :" + pot.show()+" pot hashcode is "+pot.hashCode());
Log.d(TAG, " poti is :" + poti.show()+" poti hashcode is "+poti.hashCode());
}
打印相同的數據,美滋滋。
這時候我們看到兩個pot對象的hashcode值是一樣的,@Scope注解起作用了。
關于Scope要注意的:
1、把Scope簡單的解釋為單例還是不科學的,只是我們剛開始接觸的時候使用@Singleton從而覺得它就是單例,其實正確的理解應該是:在某個范圍里它是單例(何為作用域呢,可以看作是我們在程序中實例化的Component的生命周期的長短:如果在Application里build的那它的作用域就是整個App的生命周期,如果是在Acitivity中build的那它的作用域就跟此Acitivity的生命周期相同,如上文代碼中的ActivityScope將Pot注入另一個activity,pot.hashcode會再次改變);
2、Scope只是一個標注,跟它的名字無關,跟它使用的地方及Component實例化的地方有關,Scope在很大程度上是為了方便閱讀代碼;
3、在Component依賴Component的時候,Scope的名字必須是不同的,這就需要自定義Scope。
- @Component的dependencies與@Component自身的scope不能相同,即組件之間的scope不能相同,否則出現下面錯誤。
- 而在Component中標注@Scope,provide方法沒有標注,那么這個Scope就不會起作用,而Component上的Scope的作用也只是為了能順利通過編譯,就像我剛剛定義的ActivityScope一樣。
- @Singleton的組件不能依賴其他scope的組件,但是其他scope的組件可以依賴@Singleton組件。
- 沒有scope的不能依賴有scope的組件。
那么,我們如何使用Dagger2實現單例呢?
很簡單,做到以下兩點即可。
依賴在Component中是單例的(供該依賴的provide方法和對應的Component類使用同一個Scope注解。)
-
對應的Component在App中只初始化一次,每次注入依賴都使用這個Component對象。(在Application中創建該Component)
上代碼
修改app中AndApplication
public class AndApplication extends Application { private PotComponent potComponent; @Override public void onCreate() { super.onCreate(); initializeARouter(); initializePotComponent(); } private void initializePotComponent() {//PotComponent位全局變量,且只有一個 potComponent = DaggerPotComponent.builder() .flowerComponent(DaggerFlowerComponent.create()) .build(); } public PotComponent getPotComponent() { return potComponent; } }
Activity注入代碼改為即可
DaggerDaggerActivityComp.builder().potComponent(( (AndApplication) getApplication()).getPotComponent()). daggeActivityModule(new DaggeActivityModule(3000)).build().inject(this);
現在Pot的生命周期就和app相同了。
總結
1、Module并不是必需的,但Component是必不可少的;
2、編譯后生成的Component實現類的名稱是Dagger+我們所定義的Component接口的名稱。
3、Dagger2的依賴注入思想重在理解,希望小白們上手以后不單單是在使用它,更重要的是要理解它;Dagger2還有很多其他花式用法,比如在文里提到的一個Module兩個方法返回值相同,還有懶加載等等,希望大家自己研究一下,以防不時之需。
在使用dagger2的過程中,在定義一些類或方法的名字的時候,要遵守一些谷歌提出的固定標準,以方便代碼閱讀與維護:
1、定義的Component和Module的名字是無所謂的,但是一般遵照以Component或Module結尾的名稱;
2、Module中用@Provides標注的方法的方法名是無所謂的,返回值是最重要的,但是一般遵照以provide開頭的方法名;
3、Component中返回值為void且有參的方法,方法名是無所謂的,參數是最重要的代表的是要注入的目標位置,但是方法名一般為inject;
4、Component中返回值不為void且無參的方法,方法名是無所謂的,返回值是最重要的代表的是暴露給子Component使用的依賴或者是獲取的子Component的類型。
參考博文:
1,可能是東半球最好的dagger2文章
2, 淺析Dagger2的使用