原文鏈接:https://www.jianshu.com/p/04d9db541155
目錄
- 一:Dagger2是什么?
- 二:為什么要有Dagger2
- 三:Dagger2如何使用
- 基本的概念
- 如何使用Dagger2
- 高級用法
(1)構造方法需要其他參數時候
(2) 模塊之間的依賴關系
(3) @Named注解使用
(4) @Singleton注解
(5)自定義Scoped
(6)Subcomponent
(7)lazy 和 Provider
- 四: MVP + Dagger2
Ps:文末有架構師進階資料和面試題資料
一:Dagger2是什么?
是一個依賴注入框架,butterknife也是一個依賴注入框架。不過butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是對象的管理,其目的是為了降低程序耦合。
# 二:為什么要有Dagger2

下面我就手寫了
public class A { public void eat() { System.out.print("吃飯了"); } }
使用的時候我們就要
A a = new A(); a.eat();
如果現在改了,早A的構造方法中必須傳入B對象
public class A { private B b; public A(B b) { this.b = b; } public void eat() { System.out.print("吃飯了"); } }
那么使用的時候
A a = new A(new B()); a.eat();
可能就有人說了,不就加一個對象么,這里只是我舉的一個很簡單的例子,看的感覺很簡單,但是在實際開發中,如果現在改了一個這個構造方法。是不是意味着,整個項目中的都的改,一不小心, 就是BUG 啊
三:Dagger2如何使用
1. 基本的概念
上來給你說,怎么玩,肯定懵逼,這里我簡單說一下幾個概念,想有個認知,在往下看,會好很多,Dagger 是通過@Inject使用具體的某個對象,這個對象呢,是由@Provides注解提供,但是呢,這個@Provides只能在固定的模塊中,也就是@Module注解,我們查找的時候,不是直接去找模塊,而是去找@Component

我們反向推導,當我們使用
@Inject A a
想要獲取a對象的示例的時候,Dagger2 會先去找,當前Activity或者Fragment所連接的橋梁,例如上圖中,連接的只有一個橋梁,實際上可以有多個,這個橋梁,會去尋找他所依賴的模塊,如圖中,依賴了模塊A,和模塊B,然后在模塊中,會去尋找@Providers注解,去尋找A的實例化對象。
2. 如何使用Dagger2
(1) 引入依賴庫
Dagger2官網
compile 'com.google.dagger:dagger:2.11' annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
(2) 創建Moudule
//第一步 添加@Module 注解 @Module public class MainModule { }
(3)創建具體的示例
//第一步 添加@Module 注解 @Module public class MainModule { //第二步 使用Provider 注解 實例化對象 @Provides A providerA() { return new A(); } }
(4)創建一個Component
//第一步 添加@Component //第二步 添加module @Component(modules = {MainModule.class}) public interface MainComponent { //第三步 寫一個方法 綁定Activity /Fragment void inject(MainActivity activity); }
(5)Rebuild Project

然后AS 會自動幫我們生成一個

開頭都是以Dagger開始的
(6)將Component與Activity/Fragment綁定關系
package com.allens.daggerdemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.allens.daggerdemo.Bean.A; import com.allens.daggerdemo.component.DaggerMainConponent; import javax.inject.Inject; public class MainActivity extends AppCompatActivity { /*** * 第二步 使用Inject 注解,獲取到A 對象的實例 */ @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /*** * 第一步 添加依賴關系 */ //第一種方式 DaggerMainConponent.create().inject(this); //第二種方式 DaggerMainConponent.builder().build().inject(this); /*** * 第三步 調用A 對象的方法 */ a.eat(); } }
肯定有小伙伴說了,為了拿到一個對象,這么大個彎,太麻煩了。別急慢慢看,路要一步一步走嘛
3. 高級用法
(1)構造方法需要其他參數時候
怎么說呢,就和最來時的意思一樣,
A a = new A(new B()); a.eat();
這種情況,如何使用Dagger2呢
肯定有小伙伴這么想
@Provides A providerA() { return new A(new B()); }
直接 new 一個B ,這樣的使用方法,是不對的!!!!!!,不對的!!!!!!!,不對的!!!!!!!!!
正確的打開方式
這時候,我們什么都不用該,只需要在moudule中添加一個依賴就可以了
@Module public class MainModule { /*** * 構造方法需要其他參數時候 * * @return */ @Provides B providerB() { return new B(); } @Provides A providerA(B b) { return new A(b); } }
(2) 模塊之間的依賴關系

模塊與模塊之間的聯系,
@Module (includes = {BModule.class})// includes 引入) public class AModule { @Provides A providerA() { return new A(); } }
這樣的話,Dagger會現在A moudule 中尋找對象,如果沒找到,會去找module B 中是否有被Inject注解的對象,如果還是沒有,那么GG,拋出異常
一個Component 應用多個 module
@Component(modules = {AModule.class,BModule.class}) public interface MainComponent { void inject(MainActivity activity); }
dependencies 依賴其他Component
@Component(modules = {MainModule.class}, dependencies = AppConponent.class) public interface MainConponent { void inject(MainActivity activity); }
注意 這里有坑。一下會講解
(3) @Named注解使用
相當於有個表示,雖然大家都是同一個對象,但是實例化對象不同就不如
A a1 = new A(); A a2 = new A(); // a1 a2 能一樣嘛
Module中 使用@Named注解
@Module public class MainModule { private MainActivity activity; public MainModule(MainActivity activity) { this.activity = activity; } @Named("dev") @Provides MainApi provideMainApiDev(MainChildApi mainChildApi, String url) { return new MainApi(mainChildApi, activity,"dev"); } @Named("release") @Provides MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) { return new MainApi(mainChildApi, activity,"release"); } }
在Activity/Fragment中使用
public class MainActivity extends AppCompatActivity { @Named("dev") @Inject MainApi apiDev; @Named("release") @Inject MainApi apiRelease; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainComponent.builder() .mainModule(new MainModule(this)) .mainChildModule(new MainChildModule()) .build() .inject(this); apiDev.eat(); apiRelease.eat(); Log.i("TAG","apiDev--->" + apiDev); Log.i("TAG","apiRelease--->" + apiRelease); } }
打印Log
07-14 01:46:01.170 2006-2006/? I/TAG: apiDev--->com.allen.rxjava.MainApi@477928f 07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->com.allen.rxjava.MainApi@f2b291c
(4) @Singleton注解
單利模式,是不是超級方便,你想然哪個對象單利化,直接在他的Provider上添加@Singleton 就行了
例如
@Singleton @Provides A providerA(B b) { return new A(b); }
注意: 第一個坑!!!
如果 moudule所依賴的Comonent 中有被單利的對象,那么Conponnent也必須是單利的
@Singleton @Component(modules = {MainModule.class}) public interface MainConponent { }
然后 在Activity中使用,直接打印a1 a2 的地址,
@Inject A a2; @Inject A a1;
可以看到Log
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba 12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
不相信的小伙伴可以吧@Singleton去掉試試
現在我們完成了單利,然后做了一個事情,就是點擊某個按鈕,跳轉到一個新的Activiry,兩邊都引用同樣一個A 對象,打印A 的地址,
說一下,一個Conponent 可以被對個Activity/Fragment 引用,如
@Singleton @Component(modules = {MainModule.class}) public interface MainConponent { void inject(MainActivity activity); void inject(TestAct activity); }
上面與兩個Activity, MainActivity 和 TestAct ,都引用相同的 對象,答應地址看看
12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba 12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->com.allens.daggerdemo.Bean.A@4f81861
竟然不同,說好的單利呢
注意: 第二個坑,單利對象只能在同一個Activity中有效。不同的Activity 持有的對象不同
那有人就要問了,沒什么辦法么,我就想全局只要一個實例化對象啊? 辦法肯定是有的,
(5) 自定義Scoped
/** * @作者 : Android架構 * @創建日期 :2017/7/14 下午3:04 * @方法作用: * 參考Singleton 的寫法 * Scope 標注是Scope * Documented 標記在文檔 * @Retention(RUNTIME) 運行時級別 */ @Scope @Documented @Retention(RUNTIME) public @interface ActivityScoped { }
首先想一下,什么樣的對象,能夠做到全局單例,生命周期肯定和APP 綁定嘛,這里我做演示,一個AppAip 我們要對這個對象,全局單利,所以二話不說,先給Application 來個全家桶,
Module
@Module public class AppModule { @Singleton @Provides AppApi providerAppApi() { return new AppApi(); } }
Component
@Singleton @Component(modules = AppModule.class) public interface AppComponent { AppApi getAppApi(); }
Application
public class MyApp extends Application { private AppConponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppConpoment.create(); } public AppConponent getAppComponent() { return appConponent; } }
最后是如何使用
首先,這個是個橋梁,依賴方式,上文已經說過了
@ActivityScoped @Component(modules = {MainModule.class}, dependencies = AppConponent.class) public interface MainComponent { void inject(MainActivity activity); void inject(TestAct activity); }
細心的小伙伴可能已經發現了,只有在上面一個MainComponent添加了一個@ActivityScoped,這里說明一下,@Singleton是Application 的單利
注意,第三個坑,子類component 依賴父類的component ,子類component的Scoped 要小於父類的Scoped,Singleton的級別是Application
所以,我們這里的@Singleton 級別大於我們自定義的@ActivityScoped,同時,對應module 所依賴的component ,也要放上相應的Scope
好吧,上面的例子,打印Log.
12-30 02:16:30.899 4717-4717/? E/TAG: A1---->com.allens.daggerdemo.Bean.AppApi@70bfc2 12-30 02:16:31.009 4717-4717/? E/TAG: A2---->com.allens.daggerdemo.Bean.AppApi@70bfc2
一樣啦
爬坑指南(極度重要)
- Provide 如果是單例模式 對應的Compnent 也要是單例模式
- inject(Activity act) 不能放父類
- 即使使用了單利模式,在不同的Activity 對象還是不一樣的
- 依賴component, component之間的Scoped 不能相同
- 子類component 依賴父類的component ,子類component的Scoped 要小於父類的Scoped,Singleton的級別是Application
- 多個Moudle 之間不能提供相同的對象實例
- Moudle 中使用了自定義的Scoped 那么對應的Compnent 使用同樣的Scoped
(6)Subcomponent
這個是系統提供的一個Component,當使用Subcomponent,那么默認會依賴Component
例如
@Subcomponent(modules = TestSubModule.class) public interface TestSubComponent { void inject(MainActivity activity); } @Component(modules = {MainModule.class}) public interface MainConponent { TestSubComponent add(TestSubModule module); }
在TestSubComponent中 我void inject(MainActivity activity);,便是這個橋梁,我是要注入到MainActivity,但是dagger 並不會給我生成一個Dagger開頭的DaggerTestSubComponent 這個類,如果我想使用TestSubModule.class里面提供的對象,依然還是使用DaggerMainConponent例如
DaggerMainConponent .builder() .mainModule(new MainModule()) .build() .add(new TestSubModule()) .inject(this);
可以看到這里有一個add的方法,真是我在MainConponent添加的TestSubComponent add(TestSubModule module);
(7)lazy 和 Provider
public class Main3Activity extends AppCompatActivity { @PresentForContext @Inject Lazy<Present> lazy; @PresentForName @Inject Provider<Present> provider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main3); AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); ActivityComponent activityComponent = DaggerActivityComponent.builder() .appComponent(appComponent) .activityModule(new ActivityModule()) .build(); activityComponent.injectActivity(this); Present present = lazy.get(); Present present1 = provider.get(); } }
其中Lazy(懶加載)的作用好比component初始化了一個present對象,然后放到一個池子里,需要的時候就get它,所以你每次get的時候拿到的對象都是同一個;並且當你第一次去get時,它才會去初始化這個實例.
procider(強制加載)的作用:
1:同上當你第一次去get時,它才會去初始化這個實例
2:后面當你去get這個實例時,是否為同一個,取決於他Module里實現的方式
四: MVP + Dagger2
這是現在主流的設計架構
MVP,這個我會在后面的文章介紹,這里不做太多解釋
當你了解MVP 的時候,你就知道,所有的業務邏輯全在Presenter,
換句話, presenter 持有的對象,控制着你程序的全部邏輯,這在dagger 中,講白了 我們只要將所有的presetner 對象控制就可以了
下面附上目錄結構,當然僅僅作為參考。dagger 強大的用法還是需要各位自己去體會,下面的項目 是我剛剛學會dagger 時候 寫的一個項目


可以看到 ,我是 將所有的activity 或者 fragment 全部添加在同一個Component中,當然現在的話 不推薦,比如Utils 你可以專門做一個Component,
首先放上我的Module,公司項目,很多東西沒敢放上來,體諒,可以看到 我這里提供了一個SplashPresenter,也就是啟動頁的Presneter,業務邏輯
@Module public class ApiModule { public ApiModule() { } @Provides @Singleton Handler provideHandler() { return new Handler(); } @Provides @Singleton SQLiteDatabase provideSQLiteDatabase() { return new DataBaseHelper(MyApp.context, Config.SqlName, null, Config.SqlVersion).getWritableDatabase(); } /** * @ User : Android架構 * @ 創建日期 : 2017/7/13 下午3:24 * @模塊作用 : * <p> * ==================================================================================================================================== * ==================================================================================================================================== */ private SplashPresenter splashPresenter; public ApiModule(SplashAct splashAct) { splashPresenter = new SplashPresenter(splashAct, new SplashModel()); } @Provides @Singleton SplashPresenter provideSplashPresenter() { return splashPresenter; } ..... }
當我使用的時候,只需要注入即可,如下代碼
public class SplashAct extends BaseActivity implements SplashContract.View { @Inject SplashPresenter presenter; @Inject Handler handler; @Inject ApiService apiService; @Override protected void onCreate() { setContentView(R.layout.activity_splash); } @Override protected void initInject() { DaggerApiComponent.builder() .apiModule(new ApiModule(this)) .build() .inject(this); } @Override protected void initListener() { presenter.getWordsInfo(true, apiService); } @Override public void gotoLogInAct() { handler.postDelayed(new Runnable() { @Override public void run() { startActivity(new Intent(SplashAct.this, LogInAct.class)); finish(); } }, 1500); } }