1. 程式人生 > >Dagger2進階必備技能

Dagger2進階必備技能

之前寫過一篇文章介紹Dagger2的初步知識, 本篇文章主要介紹Dagger2的進階知識點.

主要包含的內有有

  • @Binds與@Provides的使用
  • Provider與Lazy的使用
  • 依賴與包含
  • Dagger.Android

@Binds與@Provides

相信大家經常會使用@Provides來在Module裡面提供需要注入物件的構造, 但從來沒有用過@Binds.

如果我們需要注入一個介面的實現,我們常常會這麼做:

@Provides
public XXInterface providesXX(XXImp imp) {
    return imp;
}

其實這樣的程式碼可以通過@Binds

簡化為

@Binds
public abstract XXInterface bindXX(XXImp imp);

同時你需要將你的Module改為abstract即可,但是要注意這兩個不能共存於一個Module,是不是很簡單.

Provider與Lazy

大家想必會使用過Lazy,很多語言都有Lazy,如最近大紅大紫的Kotlin就可以通過by lazy {}來實現物件的延遲載入.

沒錯,Lazy<T>也是如此,只有當你呼叫get()時,才會真正注入.

Provider<T>與之的區別在於,Lazy延遲載入之後每次的呼叫都是同一個物件,而Provider則要看注入物件的實現,如果是通過@Scope

約束的物件,則是同一個,否則每次都會建立新的.

依賴於包含

字面意思很好理解,翻譯成Dagger的術語就是dependencysubcomponent.

下面分別舉例子來說明其中的區別和使用方法.

Dependency

AnimalComponent

@Component(
        dependencies = FoodComponent.class,
        modules = AnimalModule.class
)
public interface AnimalComponent {
    Animal getAnimal();
}

AnimalModule

@Module
class AnimalModule {
    @Provides
    public Animal providesAnimal(Food food) {
        //Animal需要另外一個Component提供的Food來建立
        return new Animal(food);
    }
}

FoodComponent

@Component(modules = FoodModule.class)
public interface FoodComponent {
    //這個是關鍵,必須顯示指出可以提供Food物件的生成
    Food getFood();
}

這樣我們可以通過傳入FoodComponent來完成注入, 如下:

DaggerAnimalComponent.builder().foodComponent(foodComponent).build().getAnimal()

Subcomponent

與依賴不同,Subcomponent擁有主Component所有注入物件,也就是說Subcomponent可以注入更多的物件, 通過生成程式碼也可以看出, 它的實現是主Component的內部類.

Cat

@Subcomponent(modules = {CatModule.class})
public interface CatComponent {
    Cat getCat();
}

CatModule

@Module
public class CatModule {
    @Provides
    public Cat providesCat(Leg leg//Animal Component提供) {
        return Cat(leg);
    }
}

我們還必須在AnimalComponent顯示提供CatComponent,因為如上所述,Cat是Animal的內部類了.

@Component(
        dependencies = FoodComponent.class,
        modules = AnimalModule.class
)
public interface AnimalComponent {
    Animal getAnimal();
    CatComponent createCatComponent();
}

這樣我們就可以通過下面的辦法來實現Cat的注入:

DaggerAnimalComponent.build().createCatComponent().getCat();

Subcomponent with explicit builder

當我們AnimalComponent需要對Cat進行修改再輸出的話(如指定貓的名字),可能就需要為CatComponent提供Builder

@Subcomponent(modules = {CatModule.class})
public interface CatComponent {
    @Subcomponent.Builder
    interface Builder {
        @BindsInstance Builder name(String name);
        CatComponent build();
    }
}

然後我們需要在AnimalModule裡面使用這個Builder

@Module(subcomponents = CatComponent.class)//注意這裡需要加上這一條宣告
class AnimalModule {
    @Provides
    public Animal providesAnimal(Food food) {
        //Animal需要另外一個Component提供的Food來建立
        return new Animal(food);
    }
    @Provides
    public CatComponent providesCatComponent(CatComponent.Builder builder) {
        //這裡只是舉個例子,可能這裡的Cat構造依賴於Animal的另外屬性
        return builder.name("喵喵").build();
    }
}

Dagger.Android

平時我們注入的時候常常都是將Component存在Application裡面,然後在Acitivity的onCreate或者Fragment的onAttach, 通過靜態物件Applicate.component.inject(xxx)或者((XXApplication)getApplication()).getComponent().inject(xxx)來注入.

我們常常需要記住在固定的生命週期裡面呼叫固定的語句,如果你的Activity或者Fragment在別的module裡面使用公開的介面,對於Fragment你還可以對其物件進行注入(inject(fragmentInstance)),然而對Activity可能就沒有很好的辦法了...

Google的Dagger2提供了一套針對Android的東西,幫助你只需要呼叫AndroidInjection.inject(this)或者AndroidSupportInjection.inject(this)來注入,甚至還可以通過新增一些監聽器,達到自動注入的效果哦.

下來看看如何實現吧

新增依賴

//x>=10
implementation 'com.google.dagger:dagger-android:2.x'
// if you use the support libraries
implementation 'com.google.dagger:dagger-android-support:2.x'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'

引入AndroidInjectionModule.class到你的Component

提供繼承AndroidInjector<T>Subcomponent, 及其Builder

@Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
  @Subcomponent.Builder
  public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}

通過Builder提供對應ActivityAndroidInjector.Factory

@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
  @Binds
  @IntoMap
  @ActivityKey(YourActivity.class)
  abstract AndroidInjector.Factory<? extends Activity>
      bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
}

@Component(modules = {..., YourActivityModule.class})
interface YourApplicationComponent {}

Tips

類似於之前介紹Subcomponent.Builder, 如果你不需要定製該Builder, 如新增方法之類的, 那麼這上面兩步可以做簡化.

@Module
abstract class YourActivityModule {
    @ContributesAndroidInjector(modules = { /* modules to install into, like FragmentModule */ })
    abstract YourActivity contributeYourActivityInjector();
}

引數module可以把想要注入的Fragment抽象到YourFragmentModule一併注入.

實現HasActivityInjector

public class YourApplication extends Application implements HasActivityInjector {
  @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

  @Override
  public void onCreate() {
    super.onCreate();
    DaggerYourApplicationComponent.create()
        .inject(this);
  }

  @Override
  public AndroidInjector<Activity> activityInjector() {
    return dispatchingActivityInjector;
  }
}

實際上所有的注入構造的工場方法AndroidInjector.Factory都被存入了一個Map儲存在DispatchingAndroidInjector,我們可以檢視其生成的程式碼,其中核心邏輯在maybeInject(T instance)

public boolean maybeInject(T instance) {
    Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
        injectorFactories.get(instance.getClass());
    if (factoryProvider == null) {
      return false;
    }
    AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
    try {
      AndroidInjector<T> injector = factory.create(instance);
      injector.inject(instance);
      return true;
    } catch (ClassCastException e) {
        ...
    }
  }

這就是其之所以能通過AndroidInjection.inject(this)實現注入的核心原理所在,Fragment同理.

當然你需要通過持有AndroidInjector Module的Component將這個DispatchingAndroidInjector注入了才行,一般可以在Application裡面做.

實現自動注入.

由於使用Dagger.android擴充套件使注入入口得到統一,那麼就可以通過新增監聽的方式在activity與fragment建立的時候實現自動注入.

當然相信之後此部分程式碼可能會被融入進Dagger2.

Application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                        handleActivity(activity);
                    }
                    ....
                }
                

private static void handleActivity(Activity activity) {
        if (activity instanceof HasSupportFragmentInjector) {
            AndroidInjection.inject(activity);
        }
        if (activity instanceof FragmentActivity) {
            ((FragmentActivity) activity).getSupportFragmentManager()
                    .registerFragmentLifecycleCallbacks(
                            new FragmentManager.FragmentLifecycleCallbacks() {
                                @Override
                                public void onFragmentCreated(FragmentManager fm, Fragment f,
                                        Bundle savedInstanceState) {
                                    if (f instanceof Injectable) {
                                        AndroidSupportInjection.inject(f);
                                    }
                                }
                            }, true);
        }
    }

總結

Dagger2.Android2.10版本後推出,只有不到三個月,可見Dagger2還在不斷自我強大的過程中,它的出現使得Android開發在很多層面變的簡單,如果有希望進一步學習的朋友,可以參考官方文件和一些Google的Sample,會在最後的Reference給出連線.

Demo

Reference

  • https://google.github.io/dagger/android.html
  • https://google.github.io/dagger/subcomponents.html
  • https://github.com/googlesamples/android-architecture-components