Dagger2是一款基於 Java 註解來實現的徹底在編譯階段完成依賴注入的開源庫。Dagger2 應於 Java 和 Android 開發而不僅僅是 Android,主要用於模塊間解耦、提升代碼的健壯性和可維護性。java
在app/build.gradle中的dependencies{ }加入如下代碼同步便可:android
implementation 'com.google.dagger:dagger:2.23.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'
dagger2三個重要組成部分爲:module、component,、container,三者的關係以下圖所示:json
因爲Dagger2主要是編譯期對註解內容進行獲取,因此這三個組成部分也有相應的註解符號。app
Containeride
@Inject : 用於標記須要注入的依賴。函數
Modulegradle
@Module : 用該註解修飾的類,裏面主要提供該依賴對象的實例化方法,方法名通常爲provide + 對象名。ui
@Provides : 用這個來修飾@Module註解類中的方法,代表要提供的實例化對象this
Componetgoogle
@Componet : 能夠理解爲注入器,它會把目標類依賴的實例注入到目標類中。。
在構建好註解以後,項目須要rebuild一下,整個依賴注入的過程就算完成。
一、首先編寫Person類,使用@Inject註解在構造函數上,用於標記須要注入的依賴。以下所示:
public class Person { private static final String TAG = "Person"; @Inject public Person() { } public void eat(){ Log.e(TAG, "eat...."); } }
二、接下來用@Component註解來完成依賴注入。咱們須要定義一個接口,接口命名建議爲:目標類+Component,在編譯後Dagger2就會爲咱們生成名爲Dagger+目標類名+Component的輔助類。具體代碼以下所示:
@Component public interface MainActivityComponent { void inject(MainActivity activity); }
Component則能夠理解爲注入器,它會把目標類依賴的實例注入到目標類中。在這裏須要定義inject方法,傳入須要注入依賴的目標類。
三、在MainActivity中使用@Inject構建Person對象
public class MainActivity extends AppCompatActivity { @Inject Person person; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); person.eat(); } }
若是項目中使用了第三方的類庫,能夠採用@Module和@Provides來處理。咱們建立GsonModule類,以下所示:
@Module public class GsonModule { @Provides public Gson provideGson(){ return new Gson(); } }
@Module標註在類上,用來告訴Component,能夠從這個類中獲取依賴對象,也就是Gson類。
@Provodes 標記在方法上,表示能夠經過這個方法來獲取依賴對象的實例。
@Module標註的類其實就是一個工廠,用來生成各類類;@Provodes標記的方法,就是用來生成這些類的實例的。接着來編寫Component類,以下所示:
@Component(modules = GsonModule.class) public interface MainActivityComponent { void inject(MainActivity activity); }
這和此前的區別就是加上了modules=GsonModule.class,用來指定Module。須要注意的是,Component 中能夠指定多個Module。接下來在MainActivity中使用Gson,以下所示:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Inject Gson gson; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); User user = new User("Legend", 31); String json = gson.toJson(user); Log.e(TAG, json); } }
其中,User類就是一個包含name和age的JavaBean,很是簡單。
還有一種狀況,當咱們使用依賴注入的時候,若是須要注入的對象是抽象的,則@Inject也沒法使用,由於抽象的類並不能實例化。這時也能夠採用@Module和@Provides。
一、定義一個Engine抽象類
public abstract class Engine { public abstract void work(); }
二、接着定義它的實現類GasolineEngine:
public class GasolineEngine extends Engine{ @Override public String work() { return "汽油發動機發動"; } }
三、隨後在Car類中引用Engine,以下所示:
public class Car { private Engine engine; @Inject public Car(Engine engine) { this.engine = engine; } public String run(){ return engine.work(); } }
四、建立EngineModule類,以下所示:
@Module public class EngineModule { @Provides public Engine provideEngine(){ return new GasolineEngine(); } }
五、接着在Component中指定EngineModule:
@Component(modules = EngineModule.class) public interface MainActivityComponent { void inject(MainActivity activity); }
六、最後在MainActivity中使用,以下所示:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Inject Car car; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); Log.e(TAG, car.run()); } }
@Qualifier 是限定符,@Named 則是@Qualifier 的一種實現。
當有兩個相同的依賴時,它們都繼承同一個父類或者均實現同一個接口。當它們被提供給高層時,Component 就不知
道咱們到底要提供哪個依賴 對象了,由於它找到了兩個。
好比在上面的汽車例子中,若是咱們再提供一個DieselEngine給它, EngineModule能夠改寫爲以下代碼所示:
@Module public class EngineModule { @Provides @Named("Gasoline") public Engine provideGasoline(){ return new GasolineEngine(); } @Provides @Named("Diesel") public Engine provideDiesel(){ return new DieselEngine(); } }
給不一樣的Provides定義不一樣的@Named,接下來在Car類中指定要採用哪一種Provides:
public class Car { private Engine engine; @Inject public Car(@Named("Diesel") Engine engine) { this.engine = engine; } public String run(){ return engine.work(); } }
上面的例子也能夠用@Qualifier來 實現,@Named傳遞的值只能是字符串;而@Qualifier則更靈活一些,@Qualifier不是直接標記在屬性上的,而是用來自定義註解的,以下所示:
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Gasoline { } @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Diesel { }
分別自定義兩個註解@Gasoline和@Diesel,接下來修改EngineModule類,以下所示:
@Module public class EngineModule { @Provides @Gasoline public Engine provideGasoline(){ return new GasolineEngine(); } @Provides @Diesel public Engine provideDiesel(){ return new DieselEngine(); } }
最後在Car類指定須要哪一種依賴:
public class Car { private Engine engine; @Inject public Car(@Gasoline Engine engine) { this.engine = engine; } public String run(){ return engine.work(); } }
@Scope是用來自定義註解的,而@Singleton則是用來配合實現局部單例和全局單例的。
首先在 GsonModule 中添加@Singleton,以下所示:
@Module public class GsonModule { @Singleton @Provides public Gson provideGson(){ return new Gson(); } }
接下來在MainActivityComponent中添加@Singleton:
@Singleton @Component(modules = GsonModule.class) public interface MainActivityComponent { void inject(MainActivity activity); }
咱們在MainActivity中打印Gson的hashCode值,就會發現值是相同的,以下所示:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Inject Gson gson; @Inject Gson gson1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); Log.e(TAG, gson.hashCode() + "--------" + gson1.hashCode()); } }
Gson 在 MainActivity 中是單例,若是在其餘Activity中則再也不是單例的形式。(局部單例)
若是想要實現全局單例,就須要保證對應的Component只有一個實例。能夠用@Singleton結合Application來實現:
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ApplicationScope { }
接下來在GsonModule中使用@ApplicationScope:
@Module public class GsonModule { @ApplicationScope @Provides public Gson provideGson(){ return new Gson(); } }
爲了處理多個Activity,咱們建立ActivityComponent,並使用@ApplicationScope,以下所示:
@ApplicationScope @Component(modules = GsonModule.class) public interface ActivityComponent { void inject(MainActivity activity); void inject(SecondActivity activity); }
建立App類繼承自Application,用來提供ActivityComponent實例,以下所示:
public class App extends Application { ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); activityComponent = DaggerActivityComponent.builder().build(); } public static App get(Context context){ return (App) context.getApplicationContext(); } public ActivityComponent getActivityComponent() { return activityComponent; } }
最後在MainActivity中實現以下代碼:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Inject Gson gson; @Inject Gson gson1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); App.get(this).getActivityComponent().inject(this); Button btnJump = findViewById(R.id.btn_jump); btnJump.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); } }); } }
SecondActivity中的代碼也是相似的,運行程序,發現MainActivity和SecondActivity的Gson的內存地址是同樣的。
@Component也能夠用dependencies依賴於其餘Component。咱們從新建立一個Swordsman類:
public class Swordsman { @Inject public Swordsman() { } public String fighting(){ return "真是麻煩呀!"; } }
接下來,按照慣例建立SwordsmanModule和SwordsmanComponent,以下所示:
@Module public class SwordsmanModule { @Provides public Swordsman providerSwordsman(){ return new Swordsman(); } } @Component(modules = SwordsmanModule.class) public interface SwordsmanComponent { Swordsman getSwordsman(); }
隨後在此前定義的App中引入SwordsmanComponent,以下所示:
public class App extends Application { ActivityComponent activityComponent; @Override public void onCreate() { super.onCreate(); activityComponent = DaggerActivityComponent.builder().build(); DaggerSwordsmanComponent.builder().build(); } public static App get(Context context){ return (App) context.getApplicationContext(); } public ActivityComponent getActivityComponent() { return activityComponent; } }
最後咱們在SecondActivity中使用Swordsman,以下所示:
public class SecondActivity extends AppCompatActivity { private static final String TAG = "SecondActivity"; @Inject Swordsman swordsman; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); App.get(this).getActivityComponent().inject(this); String content = swordsman.fighting(); Log.e(TAG, "swordsman:" + content); } }
注意:自定義的全局Application須要在清單文件中定義:
android:name=".App"
Dagger2提供了懶加載模式,在@Inject的時候不初始化,而是使用的時候,調用get方法來獲取實例。 接着咱們改寫上面提到的Swordsman,將它改寫爲懶加載模式。
public class SecondActivity extends AppCompatActivity { private static final String TAG = "SecondActivity"; @Inject Lazy<Swordsman> swordsmanLazy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); App.get(this).getActivityComponent().inject(this); Swordsman swordsman = swordsmanLazy.get(); String content = swordsman.fighting(); Log.e(TAG, "swordsman:" + content); } }