揭開DaggerAndroid的面紗

參考資料:

  1. 官網資料:https://dagger.dev/android
  2. Dagger Android 學習:https://juejin.im/post/5cc72061e51d456e6154b4bc

前言

1. 從零開始進入Dagger2的世界 詳細講解了Dagger2 的基礎用法,這是DaggerAndroid的基礎,不懂的朋友,可以點擊去了解一下。

2. Dagger2 和 DaggerAndroid的關係:

DaggerAndroid擴展庫 是在Dagger2 的基礎上,針對Android基本組件開發定製的一套用於對象創建獲取的框架。

3. 爲啥寫這篇文章?

  1. 有的人已經發現,純粹使用Dagger2 來進行研發,類似 XXXActivity + XXXComponent + XXXModule,並沒有過多的簡化代碼,反而引入了很多重複的代碼。
  2. 另外,還有的人,看了Dagger2 後,再去看一些基於Dagger2 開發的應用,依然看不懂,比如ContributesAndroidInjector,DaggerApplication等,因爲這些都是DaggerAndroid開發庫的內容。
  3. 網上的DaggerAndroid演示文檔,理論知識都很多,但是提供的Demo很少有循序漸進的過程,因此本文檔,模擬我之前寫過的一個應用,來演示 不使用Dagger2 ,僅使用Dagger2 和使用DaggerAndroid 來進行相同開發的不同點。通過循序漸進的過程,瞭解DaggerAndroid是如何使用的。

Demo說明:

  1. Demo DaggerAndroidTest工程地址先放到 這裏 ,強烈建議配合Demo一起閱讀,提取碼爲:g0pd。如果不能下載,請留言告知。

  2. Dagger2或者DaggerAndroid其本質還是爲了創建和提供對象。因此,本Demo將演示全局靜態對象,局部靜態動態,普通對象等,在不使用Dagger2,僅使用Dagger2和使用DaggerAndroid來開發的對比。

  3. 爲了便於佈局文件簡化,我引入了DataBinding的簡單用法,在Demo裏面有相關說明,應該至少可以看懂。

  4. 本Demo 借鑑之前開發過的一個類似商店的應用。包含一個登錄頁面,商店頁面,詳情頁面和我的頁面。

  5. 類關係和數據關係說明:

    1. App類,繼承Application,提供全局單例數據 :AppData。
    2. LoginActivity,模擬登錄頁面,可獲取AppData,並擁有LoginActivityData的數據。
    3. MainActivity,主頁面,管理三個Fragment,底部爲三個Button,負責Fragment的切換。可獲取AppData,並提供MainActivityData給Fragment使用。因此MainActivityData 是個 局部單例對象。
    4. ShoppingFragment,DetailFragment和MyFragment,可獲取AppData(全局單例),MainActivityData(局部單例) 和 FragmentData。
  6. DaggerAndroidTest工程中提供了4個Module,都可以獨立運行。所有的Module中,使用的Data數據都是相同的。

    1. dagger_android: 不使用Dagger2,我們常見的創建和獲取對象的方式。
    2. dagger_android1 僅使用Dagger2時,如何構建獲取對象。
    3. dagger_android2: 不使用ContributesAndroidInjector時,構建獲取對象的方式。
    4. dagger_android3: 使用ContributesAndroidInjector時,構建獲取對象的方式。
  7. UI 截圖說明:

正式開始進入DaggerAndroid 的學習

1. 創建工程,引入依賴,建議這5個依賴的版本都相同。目前能查到的最新半包圍2.32.2,如果還有更新的版本,請自行替換。

    //使用Dagger2,具體的版本在http://central.maven.org/maven2上查找
    //目前採用最新的2.23.2版本,如果有最新版本,請自行替換
    version = [
            dagger : "2.23.2"
    ]
    //這幾個dagger的版本,最好保持一致。
    dependencies = [
            "dagger" : "com.google.dagger:dagger:${version.dagger}",
            "dagger_compiler" : "com.google.dagger:dagger-compiler:${version.dagger}",
            //後三個是DaggerAndroid相關
            "dagger_android" : "com.google.dagger:dagger-android:${version.dagger}",
            "dagger_android_processor" : "com.google.dagger:dagger-android-processor:${version.dagger}",
            "dagger_android_support" : "com.google.dagger:dagger-android-support:${version.dagger}"
    ]

2. 先演示不使用Dagger2 時,我們一般如何創建和提供數據(不感興趣的可略過這點):

演示例子在DaggerAndroidTest 工程的dagger_android Module中,可以獨立運行

  1. 全局靜態對象,在Application創建,並對外提供該對象的public 獲取方法。其他調用類,通過 (App)getApplication.getAppData() 的方式進行獲取。
//1. 在App類中創建並提供全局使用的對象。因爲App類全局唯一,因此AppData可以說是全局單例的。
public class App extends Application {
    private AppData appHelper;
    @Override
    public void onCreate() {
        super.onCreate();
        appHelper = new AppData();
    public AppData getAppHelper(){
        return appHelper;
    }
}
//2. 獲取方式:
...
App app = (App) getApplication();
AppData appData = app.getAppHelper(); //獲取到AppData數據。
...
  1. LoginActivity類中,除可以獲取AppData外,還提供LoginActivityData的創建。直接new一個對象就行。
  2. MainActivity類,除AppData外,再new一個MainActivityData對象。Fragment可以通過getActivity來獲取MainActivityData數據,這一點和AppData類似,因此MainActivityData 就是局部單例對象。
  3. XXXFragment,可以獲取ActivityData,可以獲取AppData,也提供了XXXFragmentData的創建獲取。
		//XXXFragment中獲取對象的常見方法。
        MainActivity mainActivity = (MainActivity) getActivity();
        //獲取MainActivity提供的數據。
        data.actData = mainActivity.getMainData().getInfo();
        //獲取App提供的數據
        App app = (App) mainActivity.getApplication();
        data.appName = app.getAppHelper().getAppName();
        data.appVersion = app.getAppHelper().getVersion();
        data.userName = app.getAppHelper().getUserName();
        data.userPassword = app.getAppHelper().getPassword();
        //獲取自身數據
        data.info = new XXXInfo();
  1. 這個沒啥說的,直接看DaggerAndroid工程中 dagger_android 就行了。

2. 僅使用Dagger2 時,如何創建對象獲取對象。

演示例子在DaggerAndroidTest 工程的dagger_android1 Module中,可以獨立運行

Dagger2的具體用法,在上一篇中已經說過了,這次只做簡單說明

  1. App中的注入:
    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent.builder().application(this).build();
        appComponent.inject(this);
    }
    //還要對外提供該Component接口,爲簡化,提供靜態接口。
    public static AppComponent getAppComponent(){
        return appComponent;
    }
  1. Activity中的大致注入方式:
	...
	DaggerLoginActivityComponent.builder().appComponent(App.getAppComponent()).build().inject(this);
	...
  1. 還有很多的XXXComponent,XXXModule 直接在Demo裏面有。
  2. XXXFragment的注入,沒有實現,層級不好搭建,只爲演示Dagger2 如何創建注入對象,目的已經達到。

3. 使用DaggerAndroid擴展庫,但不使用ContributesAndroidInjector,來演示對象的創建和獲取。

演示例子在DaggerAndroidTest 工程的dagger_android2 Module中,可以獨立運行

  1. 該演示Demo會採用XXXModule + XXXSubcomponent的方式設計。
  2. 先看AppData的注入獲取方式。
//1. AppComponent 可以看出來,不使用dependence,直接全部依賴XXXModule。
@Singleton
@Component(modules = {AndroidInjectionModule.class,
                AndroidSupportInjectionModule.class,
                AppModule.class,
                LoginActivityModule.class,
                MainActivityModule.class})
public interface AppComponent { ... }
//2. App類中的注入方式。這是一組固定搭配。使用DaggerAndroid時,Application需要繼承HasActivityInjector或者HasSupportFragmentInjector。
//我沒有仔細研究,應該是爲了分發在Activity或者Framgent中的注入用的。
//因爲之後的Activity中,就可以直接是用AndroidInjection.inject(this);來完成注入代碼。
public class App extends Application implements HasActivityInjector, HasSupportFragmentInjector {
    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
    @Inject
    DispatchingAndroidInjector<Fragment> dispatchingAndroidSupportInjector;
    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent.builder().application(this).build();
        appComponent.inject(this);
    }
        @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }

    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return dispatchingAndroidSupportInjector;
    }
  1. AndroidInjectionModule 和AndroidSupportInjectionModule 是DaggerAndroid提供的Module,簡單理解爲內部會使用Map的形式來提供對象。AndroidSupportInjectionModule是使用v4包的FragmentActivity時,可以添加。
  2. 在來看LoginActivity相關的代碼
//1. LoginActivityModule的實現。
@Module(subcomponents = LoginActivityComponent.class)
public abstract class LoginActivityModule {
	//@Binds用來提供接口,這在Dagger2 中已經說過了。
	//@IntoMap + @ClassKey 這是一組固定搭配。就是說如下方式提供的構造器,會映射到某個Map中。用ClassKey來區分。
	//回想上AndroidInjectionModule 裏面有Map,是不是有點聯繫了,就是添加到了這個Map裏面。
    @Binds
    @IntoMap
    @ClassKey(LoginActivity.class)
    abstract AndroidInjector.Factory<?> bindLoginActivityInjectorFactory(LoginActivityComponent.Builder builder);
    
    //如果XXXModule已經有了@Binds,再使用@Provides時,需要使用static。
    @Provides
    static LoginActivityData provideData(){
        return new LoginActivityData();
    }
//2. LoginActivityComponent 修改如下,也算是固定搭配了。AndroidInjector.Builder爲已經過時的方法。
@Subcomponent(modules = {AndroidInjectionModule.class})
public interface LoginActivityComponent extends AndroidInjector<LoginActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<LoginActivity>{
    }
}
//3. LoginActivity的onCreate接口中,增加入住代碼,之後就可以使用AppData和LoginData了。
	...
	AndroidInjection.inject(this);
	...
  1. 參考LoginActivity相關的代碼,依葫蘆畫瓢,可以和容易的寫出MainActivity相關的注入代碼。
  2. XXXFragment的注入我們沒有繼續實現,因爲有AppData的全局單例數據和LoginActivity這樣的普通數據,已經可以說明一些注入步驟了。

4. 使用DaggerAndroid擴展庫,並使用ContributesAndroidInjector來演示對象的創建和注入(這是重點內容哦~)

演示例子在DaggerAndroidTest 工程的dagger_android3 Module中,可以獨立運行

  1. 在dagger_android2 中,依然存在XXXActivity + XXXModule + XXXSubcomponent的組合,重複代碼還是挺多的,而且固定代碼也很多,怎麼辦?使用ContributesAndroidInjector就可以再一次簡化代碼。
  2. 先看Application相關代碼,對比上面的dagger_android2,少了一些代碼了。
//1. AppModule類不做修改,AppComponent的實現。和上面的相比,需要繼承AndroidInjector<App>。
@AppScope
@Component(modules = {AndroidInjectionModule.class, AndroidSupportInjectionModule.class,
        AppModule.class,
        BuildsActivityModule.class})
public interface AppComponent extends AndroidInjector<App> {
    void inject(App app);

	//下面這一堆是個固定搭配,目的給AppModule中的@Provide接口,提供外部參數。可以自行修改。
    @Component.Builder
    interface Builder {
        AppComponent build();
        @BindsInstance Builder application(Application app);
    }
}
//2. App類,繼承DaggerApplication,接口中實現注入代碼即可。注意繼承dagger.android.support.DaggerApplication.
public class App extends DaggerApplication {
    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerAppComponent.builder().application(this).build(); //在該位置注入。
    }
}
  1. 多了一個BuildsActivityModule,開始使用ContributesAndroidInjector自動構造對象,本例中構造了LoginActivity和MainActivity類。同時這兩個類,都可以有自己XXXModule,來提供自身使用的數據。
@Module
public abstract class BuildsActivityModule {
    /**
     * TODO 知識點:添加了 @ContributesAndroidInjector 後,就不需要 @IntoMap 和 @ClassKey 了。因爲會自動添加。
     * 如下接口,會自動生成 BuildsActivityModule_ContributeLoginActivity 類
     * 該類中會自動使用 @IntoMap 和 @ClassKey。
     * @return
     */

    @ContributesAndroidInjector(modules = LoginActivityModule.class)
    abstract LoginActivity contributeLoginActivity();

    /**
     * TODO 知識點:
     * 如果MainActivityModule需要使用@Scope,同樣需要再如下位置添加相同的作用域。
     * 否則自動生成的ActivitySubComponent 會缺少@Scope。
     * @return
     */
    @ActivityScope
    @ContributesAndroidInjector(modules = {MainActivityModule.class,BuildsFragmentModule.class})
    abstract MainActivity contributeMainActivity();
}
  1. contributeMainActivity() 還依賴了個BuildsFragmentModule,如下。依然使用ContributesAndroidInjector來自動構造三個Fragment,且各自Fragment還可以擁有自己獨立使用數據的Module。
@Module
public abstract class BuildsFragmentModule {

    @FragmentScope
    @ContributesAndroidInjector(modules = ShoppingFragmentModule.class)
    abstract ShoppingFragment contributeShoppingFragment();

    @ContributesAndroidInjector(modules = DetailFragmentModule.class)
    abstract DetailFragment contributeDetailFragment();

    @ContributesAndroidInjector(modules = MyFragmentModule.class)
    abstract MyFragment contributeMyFragment();
}
  1. @Module中的每一個Activity使用@ContributesAndroidInjector標註後,會自動生成的@Module類和@Subcomponent類。比如LoginActivity,會自動生成BuildsActivityModule_ContributeLoginActivity類和BuildsActivityModule_ContributeLoginActivity.LoginActivitySubcomponent類。MainActivity也一樣,甚至Fragment也是一樣的。
  2. @ContributesAndroidInjector自動生成的Module和Subcomponent類,和上一節中的Demo中dagger_android2代碼類似。這也是上一節中演示不使用ContributesAndroidInjector的目的。
//這是使用ContributesAndroidInjector後,自動生成的@Module類和@Subcomponent類。會自動使用@Binds,@IntoMap,@ClassKey。
@Module(
  subcomponents = BuildsActivityModule_ContributeLoginActivity.LoginActivitySubcomponent.class
)
public abstract class BuildsActivityModule_ContributeLoginActivity {
  private BuildsActivityModule_ContributeLoginActivity() {}
  @Binds
  @IntoMap
  @ClassKey(LoginActivity.class)
  abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory(
      LoginActivitySubcomponent.Factory builder);
//注意:@Subcomponent以內部類的方式實現。
  @Subcomponent(modules = LoginActivityModule.class)
  public interface LoginActivitySubcomponent extends AndroidInjector<LoginActivity> {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory<LoginActivity> {}
  }
}
  1. 現在來看LoginActivity,直接繼承DaggerActivity,啥都不用寫。這其實和上一節中的LoginActivity類似,只是DaggerActivity進行了相關的封裝。DaggerActivity裏面已經使用了inject接口。
//LoginActivity只要繼承DaggerActivity就可以了,啥都不用寫,可以直接@Inject來獲取對象。
public class LoginActivity extends DaggerActivity {...}
  1. 再看MainActivity,由於沒有DaggerFragmentActivity,因此可以仿照DaggerActivity自己寫一個,也很簡單,或者用原始的方法,繼承FragmentActivity,實現HasAndroidSupportInjector接口就行。
public class MainActivity extends FragmentActivity implements HasSupportFragmentInjector, HasActivityInjector {
 @Inject
    DispatchingAndroidInjector<Activity> activityInjector;
    @Inject
    DispatchingAndroidInjector<Fragment> fragmentInjector;
    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentInjector;
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return activityInjector;
    }
	    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this); //需要手動注入代碼,一定要寫在super方法前面。
        super.onCreate(savedInstanceState);
    }
}
  1. XXXFragment也只需要繼承DaggerFragment,就可以實現對象的自動注入代碼了。唯一的不同點就是,在Fragment中,注入代碼的位置在onAttach接口中。

  2. 此時層級比較清晰,另外,任何一個項目,一定要條例清晰,這樣設計纔不會有大的偏差。

  3. DaggerAndroidTest這個Demo的擴展:這個Demo是目前常見的應用研發的一種簡單形式。實際上AppData中,可以提供一些網絡管理器的獲取和DB管理器等的創建和獲取,這樣,所有的Activity,Fragment等都可以使用。同樣的MainActivityData和XXXFragmentData都可能會提供一些複雜的邏輯類對象。

最後的總結:

  1. 經過上面的學習,DaggerAndroid擴展庫應該可以正常使用了,如果還不能正常使用,就得多寫,多調試,因爲DaggerAndroid如果不熟悉的話,很容易就會寫錯。
    一般這樣建議是:和Dagger相關的,一次性不要寫太多代碼,確保寫過的代碼可以編譯通過。是在找不出來出錯點,可以通過慢慢恢復代碼的方式來定位問題。我是用了git倉庫管理,來比對自己的改動點。
  2. 在正式的項目開發中,Dagger2 一般不會獨立使用。還會配合 MVVP模式中比如LiveData,Room,還有一些比較好的組件,比如RxAndroid等等,一起進行項目開發。
  3. 此時一定要明確每一個三方庫的作用是什麼?比如DaggerAndroid的就是 爲項目開發中創建和提供對象用的。 MVVP 只是個設計模式,是一個思想。RxAndroid是一個異步任務處理庫等等。這樣當使用多個三方庫時,就不會弄混。
  4. 有問題,可以留言,我看到了就會回覆的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章