1.遇到的問題
相信使用過Dagger
開發Android應用的小夥伴會知道(如果你還不是很瞭解Daager
,可以先看我之前的一篇基本介紹:Dagger2使用攻略),我們會在Activity
或Fragment
的生命週期方法中執行成員注入。比如這樣:
public class MyActivity extends Activity {
@Inject
Test test;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerActivityComponent
.builder()
.appComponent(((MyApplication)this.getApplication()).getAppComponent())
.build()
.inject(this);
test.getXX();
}
}
但是有一些問題:
隨着越來越多的這樣代碼塊的複製粘貼,使得以後很難重構。
更重要的是,它要求注入類型( MyActivity )知道其注入器。 即使這是通過接口而不是具體類型完成的,它打破了依賴注入的核心原則:一個類不應該知道如何注入它。
那麼我們怎麼解決他呢?
2.解決問題
不用擔心,Dagger2
在2.10版本爲我們提供瞭解決的方案。 並在2.11版本中增加了@ContributesAndroidInjector
註解,使得我們的實現更加方便。具體變化可以查看這裏
1.配置
首先將以下內容添加到您的build.gradle中:(在原先Dagger2的基礎上添加)
dependencies {
compile 'com.google.dagger:dagger-android:2.11'
compile 'com.google.dagger:dagger-android-support:2.11'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
}
2.實現
爲了更清楚的說明,我會舉一個簡單的例子使用新舊兩種寫法實現。
現在的場景是這樣的,我要在一個頁面中使用一個Login
對象,如下:
public class Login {
private String name ;
private String password ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
假設初始化name
爲小明
,password
爲******
。我們使用Dagger
進行依賴注入,實現UserModule
。
@Module
public class UserModule {
@Provides
Login provideXiaoMingUser() {
Login xiaomin = new Login();
xiaomin.setPassword("******");
xiaomin.setName("小明");
return xiaomin;
}
}
相對應的UserComponent
如下
@Component(modules = {UserModule.class})
public interface UserComponent {
void inject(SecondActivity mActivity);
}
那麼對應的SecondActivity
也就出來了。
public class SecondActivity extends AppCompatActivity {
@Inject
Login xiaoming;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
DaggerUserComponent.builder()
.userModule(new UserModule())
.build()
.inject(this);
xiaoming.getName(); //直接使用
}
以上就是我們通常的使用方式,而SecondActivity
中就體現了我們一開始說到的問題。接下來我用新方法實現一遍。
1.首先我們在AppComponent
中統一注入AndroidSupportInjectionModule
@Singleton
@Component(modules = {
AppModule.class,
StorageModule.class,
BuildersModule.class,
AndroidSupportInjectionModule.class
})
interface AppComponent extends AndroidInjector<MyApplication>{
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApplication> {}
}
AndroidInjectionModule
源碼如下 ,AndroidSupportInjectionModule
可以額外支持V4包下的Fragment
.
@Beta
@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() {}
}
可以看到它可以幫我們將安卓中四大組件以及Fragment
進行綁定。
BuildersModule
是我爲了統一管理依賴於AppComponent
的Module
添加的中間件。如下
@Module(subcomponents = {UserComponent.class})
public abstract class BuildersModule {
@Binds
@IntoMap
@ActivityKey(SecondActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindSecondActivityInjectorFactory(UserComponent.Builder builder);
}
有了BuildersModule
,我們之前的UserModule
保持不變,UserComponent
修改如下
@Subcomponent(modules = {UserModule.class})
public interface UserComponent extends AndroidInjector<SecondActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<SecondActivity> {}
}
UserComponent
需繼承自AndroidInjector
,在該子組件中含有一個抽象類Builder
,繼承自AndroidInjector.Builder
,並由@Subcomponent.Builder
註解。注意:這裏UserComponent
使用@Subcomponent
註解是爲了使用AppComponent
中的AndroidSupportInjectionModule
.
接下來自定義MyApplication
繼承DaggerApplication
public class MyApplication extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}
如果我們代碼中已經有繼承的父類Application
,則可以根據DaggerApplication
源碼去實現對應接口。
最後一步Activity
繼承DaggerAppCompatActivity
,在super.onCreate(savedInstanceState)
之前調用AndroidInjection.inject(this)
.
public class SecondActivity extends DaggerAppCompatActivity {
@Inject
Login xiaoming;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
xiaoming.getName(); //直接使用
}
當然了,我們可以將AndroidInjection.inject(this)
封裝進BaseActivity
,這樣使用起來更酸爽。
有些人會覺得這寫起來比之前麻煩多了,爲了解決這個問題,在最近的2.11版中新增了@ContributesAndroidInjector
註解。有了它我們上面的UserComponent
可以不要了。(驚不驚喜,意不意外!)
@Module
public abstract class BuildersModule {
@ContributesAndroidInjector(modules = UserModule.class)
abstract SecondActivity secondActivityInjector();
}
我們可以看一下自動生成的代碼:
@Module(subcomponents = BuildersModule_SecondActivityInjector.SecondActivitySubcomponent.class)
public abstract class BuildersModule_SecondActivityInjector {
private BuildersModule_SecondActivityInjector() {}
@Binds
@IntoMap
@ActivityKey(SecondActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
SecondActivitySubcomponent.Builder builder);
@Subcomponent(modules = UserModule.class)
public interface SecondActivitySubcomponent extends AndroidInjector<SecondActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<SecondActivity> {}
}
}
是不是一毛一樣。。。不要怪我不早說,我也是爲了大家可以更多的瞭解其中的細節。
3.工作原理
那麼它是如何工作的?(前方一大堆代碼亂入,不重要部分已去除)
我們首先從MyApplication
開始,在我們的父類DaggerApplication
幫我們實現了注入。
public abstract class DaggerApplication extends Application{
@Inject DispatchingAndroidInjector<Activity> activityInjector;
@Override
public void onCreate() {
super.onCreate();
injectIfNecessary();
}
@ForOverride
protected abstract AndroidInjector<? extends DaggerApplication> applicationInjector();
private void injectIfNecessary() {
AndroidInjector<DaggerApplication> applicationInjector =
(AndroidInjector<DaggerApplication>) applicationInjector();
applicationInjector.inject(this); //這裏注入
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityInjector;
}
}
注入時的具體內容
private Provider<AndroidInjector.Factory<? extends Activity>> bindAndroidInjectorFactoryProvider;
private void initialize(final Builder builder) {
//這裏不就是我們使用@ContributesAndroidInjector生成的Subcomponent嗎,在這裏進行了實現
this.secondActivitySubcomponentBuilderProvider =
new dagger.internal.Factory<
BuildersModule_SecondActivityInjector.SecondActivitySubcomponent.Builder>() {
@Override
public BuildersModule_SecondActivityInjector.SecondActivitySubcomponent.Builder get() {
return new SecondActivitySubcomponentBuilder();
}
};
this.bindAndroidInjectorFactoryProvider = (Provider) secondActivitySubcomponentBuilderProvider;
//再將所有的Activity對應的SubcomponentBuilder存進MapProviderFactory
this.mapOfClassOfAndProviderOfFactoryOfProvider =
MapProviderFactory
.<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>builder(1)
.put(SecondActivity.class, bindAndroidInjectorFactoryProvider)
.build();
this.dispatchingAndroidInjectorProvider =
DispatchingAndroidInjector_Factory.create(mapOfClassOfAndProviderOfFactoryOfProvider);
//將四大組件及Fragment全部放入
this.myApplicationMembersInjector =
MyApplication_MembersInjector.create(dispatchingAndroidInjectorProvider);
}
@Override
public void inject(MyApplication arg0) {
myApplicationMembersInjector.injectMembers(arg0); //注入
}
private final class SecondActivitySubcomponentBuilder
extends BuildersModule_ContributeSecondActivityInjector.SecondActivitySubcomponent.Builder {
private UserModule userModule;
private SecondActivity seedInstance;
@Override
public BuildersModule_ContributeSecondActivityInjector.SecondActivitySubcomponent build() {
if (userModule == null) {
this.userModule = new UserModule();
}
if (seedInstance == null) {
throw new IllegalStateException(SecondActivity.class.getCanonicalName() + " must be set");
}
return new SecondActivitySubcomponentImpl(this);
}
@Override
public void seedInstance(SecondActivity arg0) {
this.seedInstance = Preconditions.checkNotNull(arg0);
}
}
當我們在Activity
調用AndroidInjection.inject(this)
時,從Application
獲取一個DispatchingAndroidInjector<Activity>
,並將您的activity傳遞給inject(activity)
public static void inject(Activity activity) {
Application application = activity.getApplication();
//獲取DaggerApplication中的activityInjector
AndroidInjector<Activity> activityInjector =
((HasActivityInjector) application).activityInjector();
activityInjector.inject(activity);
}
DispatchingAndroidInjector
通過AndroidInjector.Factory
創建AndroidInjector
,並將您的activity傳遞至SecondActivitySubcomponentImpl
中。
@Beta
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
@Inject
DispatchingAndroidInjector(
Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) {
this.injectorFactories = injectorFactories;
}
@CanIgnoreReturnValue
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
@SuppressWarnings("unchecked")
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
try {
AndroidInjector<T> injector =
checkNotNull(
factory.create(instance),//<-創建
"%s.create(I) should not return null.",
factory.getClass().getCanonicalName());
injector.inject(instance);//傳遞
return true;
} catch (ClassCastException e) {
}
}
@Override
public void inject(T instance) {
boolean wasInjected = maybeInject(instance);
if (!wasInjected) {
throw new IllegalArgumentException(errorMessageSuggestions(instance));
}
}
}
private final class SecondActivitySubcomponentImpl
implements BuildersModule_ContributeSecondActivityInjector.SecondActivitySubcomponent {
private Provider<Login> provideXiaoMingUserProvider;
private MembersInjector<SecondActivity> secondActivityMembersInjector;
private SecondActivitySubcomponentImpl(SecondActivitySubcomponentBuilder builder) {
assert builder != null;
initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final SecondActivitySubcomponentBuilder builder) {
this.provideXiaoMingUserProvider =
UserModule_ProvideXiaoMingUserFactory.create(builder.userModule);
this.secondActivityMembersInjector =
SecondActivity_MembersInjector.create(
DaggerAppComponent.this.dispatchingAndroidInjectorProvider6,
DaggerAppComponent.this.dispatchingAndroidInjectorProvider3,
provideXiaoMingUserProvider);
}
@Override
public void inject(SecondActivity arg0) {
secondActivityMembersInjector.injectMembers(arg0);//注入
}
}
到此爲止,它的工作流程就是這樣。
細心地你其實這裏會發現,我們依賴的
Module
不需要我們像之前一樣去一個一個創建好去設置了,會默認實現它的無參構造方法。當然在這個Module
中可以使用我們注入的Activity
。如果你之前的Module
有構造方法,試着去修改它、
4.其他
以上是以Activity
作爲例子實現的,對於其他的四大組件以及Fragment
,使用起來大同小異。比如使用Fragment
,我們就將@ActivityKey
替換爲@FragmentKey
。AndroidInjection.inject(this)
方法,在Fragment
的onAttach()
中的super.onAttach()
方法之前調用。當然如果你使用了@ContributesAndroidInjector
則可以不用去管@xxxKey
。
@Module(subcomponents = {TestFragmentComponent.class})
public abstract class FragmentBuildersModule {
@Binds
@IntoMap
@FragmentKey(TestFragment.class) //<-- 這裏
abstract AndroidInjector.Factory<? extends Fragment>
bindTopFragmentInjectorFactory(TestFragmentComponent.Builder builder);
}
@Subcomponent(modules = TestFragmentModule.class)
public interface TestFragmentComponent extends AndroidInjector<TestFragment> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<TestFragment> {
}
}
@Module
public class TestFragmentModule {
}
public class TestFragment extends Fragment {
@Override
public void onAttach(Activity activity) {
AndroidInjection.inject(this); //<--這裏
super.onAttach(activity);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
}
我已經將這些代碼上傳到了我的Github,大家可以在分支dagger2.11
下查看新的寫法。(主分支爲舊寫法,方便大家對比。)