前言
最近看Google新的框架sample時android-architecture-components發現了dagger2在Android平臺上新的寫法,很簡潔,值得學習。特意看了看dagger2的官方文檔Android部分,講得很詳細也很到位。因為是新姿勢,中文的學習資料還比較少,所以我決定翻譯一下官方文檔。以下是主要是對dagger2 Android部分官方文檔的翻譯,并增加了一些內容幫助理解。文檔其它部分的已經有人翻譯過了dagger2官方文檔中文。如果你還不了解dagger2,或者不太熟悉dagger2中的subcomponent和multibindings的話(以下內容需要熟悉這些內容才能看懂),可以先看一下dagger2官方文檔中文。dagger2的學習曲線還是很陡峭的,官方文檔永遠是最好的學習資料。
Android Architecture Components是2017年Google I/O 大會上新推出的應用框架,主要用于解決UI組件的生命周期和數據持久化問題,幫助我們輕易地處理配置變化(像屏幕旋轉)時數據的存儲問題。構建感知生命周期的Observer,防止內存泄漏。非常值得學習。
Architecture Components官方文檔
中文翻譯
我已經放棄使用dagger.android了,具體原因可以查看當定義Dagger2 Scope時,你在定義什么?
開始
相較于其它的依賴注入框架而言,Dagger2其中一個主要的優勢是,它不使用反射來生成其實現。這意味著它可以被用在Android應用上(意思就是不影響APP的性能)。然而,把Dagger2應用在Android平臺上,還是有一些點需要注意。
dagger.android
在Android平臺上使用Dagger的一個主要的不同是,很多類的實例化依賴于操作系統本身,像是Activity和Fragment,但是Dagger最理想的工作方式是它能夠構造所有需要注入的類的實例。所以,你必須在它們(Activity、Fragment等)的生命周期中進行成員的注入。很多類看上去跟下面類似:
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//先寫如下代碼, 否則frombulator可能為null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ...其它代碼
}
}
這么做有以下問題:
- 復制粘貼同樣的代碼使得以后想要重構變得困難。越來越多的這樣復制粘貼的代碼,開發者反而對這段代碼的作用了解的更少。
- 更加重要的是,它需要被注入類(FrombulationActivity)知道它的注入類。即使這是通過接口實現的,而不是具體的類。但是,這仍然破壞了依賴注入的的核心原則:一個類不應該對它是如何被注入的有任何的了解。
補充說明:以上這樣的代碼其實在Android平臺上并不常見,更常見的是類似如下的代碼:
@Singleton
@Component(modules= AppModule.class)
public interface AppComponent {
Application getApplication();
//其它需要暴露出來的類,供dependencies使用
}
@ActivityScope
@Component(dependencies = AppComponent::class, modules= ActivityModule.class)
interface ActivityComponent {
void inject(YourActivity activity)
}
public class YourActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerActivityComponent.builder()
.appComponent(YourApp.getAppComponent())
.activityModule(new ActivityModule(this))
.build()
.inject(this);
// ...其它代碼
}
}
也就是通過component之間依賴(dependencies)的方式來管理不同component之間的關系;而Google文檔上的例子是通過子組件(subcomponent)的方式來管理component間的關系,并且如果想使用本文所介紹的新的姿勢,就必須使用子組件的方式,拋棄原來依賴的方式。就管理component之間的關系而言,這兩種方式都是可以的,我也一直都是使用依賴的方式來構建不同的component,這么做相較于子組件的方式而言,最大的優勢就是簡單,直接依賴另外一個component,被依賴的component暴露出相應的類即可。而子組件的方式寫起來比較麻煩。關鍵是還得額外學習subcomponent的是怎么回事,光整明白component就夠累的了,還要啥subcomponent。 But,解鎖了這篇文章介紹的dagger2和Android結合的新姿勢,老司機就需要考慮這么一個問題了,需不需要換個姿勢呢?!我個人覺得,還是有必要的,原因如下:
- 符合依賴注入的的核心原則:一個類不應該對它是如何被注入的有任何的了解。這個核心原則體現在我們的代碼上就是,在Activity、Fragment等類需要注入對象時,可以直接使用@Inject注解一個對象即可,不需要生成component(subcomponent)了,使用更加的方便。
- 我們都是有追求的老司機,借此學習一下subcomponent的使用有益無害。
- 代碼量更少。
沒有最懶的程序猿,只有更懶得程序猿。
Activity的注入
- 在你的Application Component中加入AndroidInjectionModule模塊,以提供所有基本類型的綁定。
@Singleton
@Component(modules= {
AndroidInjectionModule.class,
...
})
public interface AppComponent {
...
}
AndroidInjectionModule并沒有什么特別的,只是一個普通的module,該module提供了5個Map,Map的key是Android四大組件和Fragment的class對象,Map的value是相應的注入器的工廠方法。
@Module
public abstract class AndroidInjectionModule {
@Multibinds
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
activityInjectorFactories();
@Multibinds
abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
fragmentInjectorFactories();
@Multibinds
abstract Map<Class<? extends Service>, AndroidInjector.Factory<? extends Service>>
serviceInjectorFactories();
@Multibinds
abstract Map<Class<? extends BroadcastReceiver>, AndroidInjector.Factory<? extends BroadcastReceiver>>
broadcastReceiverInjectorFactories();
@Multibinds
abstract Map<Class<? extends ContentProvider>, AndroidInjector.Factory<? extends ContentProvider>>
contentProviderInjectorFactories();
private AndroidInjectionModule() {}
}
- 聲明你的subcomponent并且實現接口AndroidInjector<YourActivity>,該subcomponent需要有一個被@Subcomponent.Builder注解的并擴展自AndroidInjector.Builder<YourActivity>的構造器:
@Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}
- 聲明過subcomponent之后,把它通過如下方式加入到主component體系中:定義一個提供該subcomponent builder的module,并且把該module加入到你的AppComponent中。
@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
@Binds
@IntoMap
@ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
}
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
YourActivityModule.class,...
})
interface AppComponent {}
注意:如果你的subcomponent和它的builder除了第2步中提及的方法或者超類沒有其它的內容,你可以用 @ContributesAndroidInjector生成2、3步中的一切。現在不需要步驟2和3,你只需聲明一個abstract module,返回你所需的activity(用 @ContributesAndroidInjector注解),可以聲明subcomponent需要的其它的module。如果這個subcomponent需要scope注解,也可以聲明:
@Module
public abstract class ActivityBulidersModule {
@ActivityScope
@ContributesAndroidInjector(modules = {/*subcomponent需要的module*/})
abstract YourActivity contributeYourActivity();
}
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
ActivityBulidersModule.class,...
})
interface AppComponent {}
@ContributesAndroidInjector注解是dagger-android-2.11中提供的,它會生成如下代碼:
@Module(subcomponents = ActivityBulidersModule_YourActivityInjector.YourActivitySubcomponent.class)
public abstract class ActivityBulidersModule_YourActivityInjector {
private ActivityBulidersModule_YourActivityInjector() {}
@Binds
@IntoMap
@ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
YourActivitySubcomponent.Builder builder);
@ActivityScope
@Subcomponent(modules = {/*subcomponent需要的module*/})
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}
}
是不是跟我們自己寫的一毛一樣,@ContributesAndroidInjector只是幫我們自動生成了一些代碼,并沒有什么特別的,但前提是第2步沒有其它方法或者超類型。
- 下一步,讓你的Application實現HasActivityInjector并且@Inject DispatchingAndroidInjector<Activity>而后從方法activityInjector()(接口HasActivityInjector中的方法)返回:
public class YourApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create()
.inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
- 最后,在 Activity.onCreate() 方法中在super.onCreate()之前調用AndroidInjection.inject(this)
public class YourActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
}
- 恭喜你!完成了!
如何工作的?
AndroidInjection.inject() 從Application中獲取了一個 DispatchingAndroidInjector<Activity>,并把activity實例傳入方法 inject(Activity)中。 DispatchingAndroidInjector 根據activity的class來查找 AndroidInjector.Factory(即 YourActivitySubcomponent.Builder),創建 AndroidInjector (即YourActivitySubcomponent), 然后把你的activity實例傳入方法 inject(YourActivity)中。
更多關于實現的原理請查看我的另外一篇文章Dagger2在Android平臺上的新魔法
Fragment注入
注入Fragment就跟注入Activity一樣。以相同的方式定義subcomponent,把Activity類型替換為Fragment,@ActivityKey替換為@FragmentKey,HasActivityInjector替換為HasFragmentInjector。
和Activity在onCreate()中注入不同,Fragment的注入在方法onAttach()中。
和為Activity添加module不同,在Fragment中你可以選擇在哪添加module。你可以把你的Fragment的subcomponent聲明為另一個Fragment component的子component,或者是Activity component的子component,或者是Application component的子component——這取決于你想在哪注入你的subcomponent。在決定了Fragment subcomponent是哪個component的子component之后,讓相應的類型實現接口HasFragmentInjector。
原文說的很繞,其實上面一段話的核心意思就是,Fragment的subcomponent可以是Fragment、Activity、Application component(或subcomponent)的subcomponent,只要其對應的類型(Fragment、Activity、Application)實現了HasFragmentInjector接口即可。作為對比,Activity則只能是Application component的subcomponent,所以只能是Application實現HasActivityInjector接口。
例如,你的Fragment subcomponent是YourActivitySubcomponent的子component。你的代碼類似于此:
public class YourActivity extends Activity
implements HasFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
@Override
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
// ...
}
@Override
public AndroidInjector<Fragment> fragmentInjector() {
return fragmentInjector;
}
}
public class YourFragment extends Fragment {
@Inject SomeDependency someDep;
@Override
public void onAttach(Activity activity) {
AndroidInjection.inject(this);
super.onAttach(activity);
// ...
}
}
@Subcomponent(modules = ...)
public interface YourFragmentSubcomponent extends AndroidInjector<YourFragment> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourFragment> {}
}
@Module(subcomponents = YourFragmentSubcomponent.class)
abstract class YourFragmentModule {
@Binds
@IntoMap
@FragmentKey(YourFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Builder builder);
}
@Subcomponent(modules = { YourFragmentModule.class, ... }
public interface YourActivityOrYourApplicationComponent { ... }
同樣我們可以簡寫:
@Module
public abstract class FragmentBuildersModule {
@ContributesAndroidInjector
abstract YourFragment contributeYourFragment();
}
Support libraries
對于使用Android support library的用戶,有dagger.android.support提供了支持。注意用支持庫中的Fragment,應該綁定AndroidInjector.Factory<? extends android.support.v4.app.Fragment>,但是仍應該實現AndroidInjector.Factory<? extends Activity> 而不是 <? extends AppCompatActivity> (或者 FragmentActivity)。dagger.android.support中有AndroidSupportInjectionModule,提供了對android.support.v4.app.Fragment的支持:
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
@Multibinds
abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
supportFragmentInjectorFactories();
private AndroidSupportInjectionModule() {}
}
如何獲取
在你的build.gradle中增加
dependencies {
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // 如果你使用support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
}
參考:
Dagger&Android