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的術語就是dependency
與subcomponent
.
下面分別舉例子來說明其中的區別和使用方法.
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
提供對應Activity
的AndroidInjector.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.Android
自2.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