写在前面
提高篇的目的是想着写一些较深入的知识,包含一些源码分析、架构设想、脚手架搭建的东西。
面向的人群是中高级的开发者或者愿意深入了解如何快速构建Kotlin&&MVVM应用的人群。
Dagger-Android
原本的打算是将其作为使用Kotlin构建MVVM应用程序系列的第五部分内容。
但因为Dagger本身就有一定的入门门槛,Dagger-Android的门槛就更高了。对于初中级开发者而言,Dagger-Android太容易入门到放弃,对于这部分人群不是很适合,因此将其放入提高篇较为合适。
又因为Dagger-Android门槛较高,对于初学者来说不适用,就如同初出江湖的小菜鸟就想着去练习远高于自己根基的武功,对自己没有太多好处,多积累基础,设计模式才是正途,能力到了自然就悟了。
又由于曲高和寡,懂的人自然懂。为此,我开通了使用Kotlin构建MVVM应用程序的小专栏,提高篇的完整内容会放在这里,愿意去了解的了解一哈。
而这里也就大致的总结一下它的思路。
为什么要有Dagger-Android?
对于这个问题,google在Dagger-Android的文档上有解释:
我们普通的dagger代码如下所示:
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
}
这会带来一些问题:
- 只是复制粘贴上面的代码会让以后的重构比较困难,还会让一些开发者不知道Dagger到底是如何进行注入的(然后就更不易理解了)
- 更重要的原因是:它要求注射类型(FrombulationActivity)知道其注射器。 即使这是通过接口而不是具体类型完成的,它打破了依赖注入的核心原则:一个类不应该知道如何实现依赖注入。
为了解决上述的问题,Dagger-Android应运而生。
这是它的起因,那么和传统的Dagger区别又在哪里呢?
区别在哪?
就在解法不同罢了,如果你认为Dagger-Android是普通Dagger的延伸,要按照Dagger的逻辑去理解Dagger-Android,那么一开始就错了。
假设把依赖注入看成是一道算法题或者数学的最后一道大题。
Dagger和Dagger-Android都是正确的答案,只是两个不同的解法而已。
相比之下,Dagger-Android在程序启动的时候通过处理注解,初始化了一个全局的单例map
Map, Provider>>
key值为activity/fragment的class,而value为提供相应Component的Provider对象。在开始的时候就为我们映射好了。
当我们调用AndroidInjection.inject(this)
时,也就是一个简单的map.get(instance.class).getComponent().inject(this)
相比之下,多了一个从map去获取component的中间过程,但是却解决了上诉的第二个问题 :一个类不应该知道如何实现依赖注入。
思路的差别就在这里,明白这一点,通过断点一步步的源码分析,Dagger-Android也没有多么高不可攀。
快速开始
首先我们需要在app/build.gradle加入相应的依赖
//dagger2 di
implementation 'com.google.dagger:dagger:2.16'
kapt 'com.google.dagger:dagger-compiler:2.16'
//dagger-android
implementation 'com.google.dagger:dagger-android:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'
kapt 'com.google.dagger:dagger-android-processor:2.16'
注入方法建议看文档更好,这里简单描述一下:
还是以MVVM-Android为例。
- 添加ActivityModule.kt
@Module
abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun contributePaoActivity(): PaoActivity
}
2 . 修改AppComponent.kt
@Singleton
@Component(modules = arrayOf(
AndroidInjectionModule::class,
AppModule::class,
ActivityModule::class)
)
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(application: PaoApp)
}
相比Dagger2,modules多了AndroidInjectionModule和ActivityModule两个类。
- rebuild一下项目,然后新增PaoApp.kt同时实现HasActivityInjector接口
class PaoApp : Application(),HasActivityInjector{
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder().application(app).build().inject(app)
}
override fun activityInjector() = dispatchingAndroidInjector
}
- 最后在Activity的onCreate()方法之前调用
AndroidInjection.inject(this)
进行注入
class PaoActivity : RxAppCompatActivity() {
@Inject
lateinit var mViewModel : PaoViewModel
override fun onCreate(savedInstanceState: Bundle?) {
//////di
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
到此,一个简单的依赖注入就好了。
当然简单是绝不简单的。这些是在写什么?完全云里雾里。刚从Dagger转换成Dagger-Android的直接就劝退了,还不如直接使用Dagger2的好。
确实,对于日常功能迭代的开发团队来说,普通的dagger更易理解,所以Dagger-Android也算是一个可选项,可以作为一个提高,而且google的很多示例里dagger的用法都是Dagger-Android,所以还是有必要懂它的原理。
原理剖析
在第四部分中,我们也了解了普通的Dagger是如何进行依赖注入的,这里我们再来回顾一次
由AppModule提供所需的依赖
由AppCompent提供注入的途径
由@Inject标识需要注入的对象
调用
DaggerAppComponent.builder()
.appModule(AppModule(applicationContext)).build()
.inject(this)
完成依赖注入
这里的逻辑比较好理解一些,就是普通的
paoActivity.mViewModel = appComponent.paoViewModel
那Dagger-Android相比之下,又是怎么样的逻辑呢?
相比之前,Dagger-Android将Activity/Fragment所需的compoent都放到了一个map对象里,这个map对象由App的dispatchingAndroidInjector
对象持有。其中key
值为activity/frament的class,value
为提供相应Component的Provider对象。
Map, Provider>>
当我们在Activity中调用 AndroidInjection.inject(this)
时,又在做什么呢?
public static void inject(Activity activity) {
checkNotNull(activity, "activity");
Application application = activity.getApplication();
if (!(application instanceof HasActivityInjector)) {
throw new RuntimeException(
String.format(
"%s does not implement %s",
application.getClass().getCanonicalName(),
HasActivityInjector.class.getCanonicalName()));
}
//找到dispatchingAndroidInjector对象
AndroidInjector activityInjector =
((HasActivityInjector) application).activityInjector();
checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());
//进行注入
activityInjector.inject(activity);
}
而activityInjector.inject(activity)
里面的逻辑又可以描述为
一个全局的单例map对象,通过key值为activity.class即
map.get(activity.class) 找到activity与之对应的Provider>
。。。
。。。
然后经过层层的深入
找到对应的component
最后依然还是调用activity.viewmodel = component.viewmodel
和普通的dagger对比一下区别就在于:
Dagger-Android在刚开始的时候通过注解处理器分析@Component、@Module、@ContributesAndroidInjector 等等注解,帮我们在App启动的时候建立了一个全局的单例map,并添加相关的映射。
可以在生成的DaggerAppComponet.kt
文件中找到
private Map, Provider>>
getMapOfClassOfAndProviderOfFactoryOf() {
return Collections
., Provider>>
singletonMap(PaoActivity.class, (Provider) paoActivitySubcomponentBuilderProvider);
}
当注入的时候就间接的通过这个map找到对应activity需要的Component,完成注入。
接下来我们就具体来看看 activityInjector.inject(activity)
是如何完成注入的。
注入过程
先来断点调试一下,看看activityInjector是什么?
)
可以看到activityInjector
的真身是DispatchingAndroidInjector
,实际调用的是DispatchingAndroidInjector
的inject()方法,接着看看inject()
方法
调用的是maybeInject(instance)
,继续深入
到这里就很清晰了,正如前文所说的那样,通过一个单例map根据key值为instance.class
找到相应的factoryProvider,通过get()
方法获取到AndroidInjector.Factory
对象
而它的真身是DaggerAppComponent
的内部类PaoActivitySubcomponentBuilder
。
private final class PaoActivitySubcomponentBuilder
extends ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder {
//....
}
PaoActivitySubcomponentBuilder 继承了ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder,再来看看ActivityModule_ContributePaoActivity
@Module(subcomponents = ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.class)
public abstract class ActivityModule_ContributePaoActivity {
private ActivityModule_ContributePaoActivity() {}
@Binds
@IntoMap
@ActivityKey(PaoActivity.class)
abstract AndroidInjector.Factory extends Activity> bindAndroidInjectorFactory(
PaoActivitySubcomponent.Builder builder);
@Subcomponent
public interface PaoActivitySubcomponent extends AndroidInjector {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder {}
}
}
这些代码是添加映射的关键代码了,Dagger-Android通过处理@ContributesAndroidInjector
自动生成的,按照Dagger-Android文档上的说法是可以自己编写,@ContributesAndroidInjector
只是简化了这步操作。
可以看到@Binds、@IntoMap、@ActivityKey 这几个注解,就如前文所说的那样将其保存到单例的map对象之中,key值便是PaoActivity.class
。
这些都是题外话,再回头继续断点调试,可以看到factory.create(instance)
。
调用了seedInstance()
方法,这是一个抽象方法,由前文的PaoActivitySubcomponentBuilder
实现。
private final class PaoActivitySubcomponentBuilder
extends ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder {
private PaoActivity seedInstance;
@Override
public void seedInstance(PaoActivity arg0) {
this.seedInstance = Preconditions.checkNotNull(arg0);
}
}
就是一个简单的赋值操作。然后返回类型为AndroidInjector
的injector
,断点可以看到它的真身是PaoActivitySubcomponentImpl
。
继续往下看,来到 injector.inject(instance);
到了这一步,就跟以前的Dagger没任何区别了。
private final class PaoActivitySubcomponentImpl
implements ActivityModule_ContributePaoActivity.PaoActivitySubcomponent {
private PaoActivitySubcomponentImpl(PaoActivitySubcomponentBuilder builder) {}
private PaoRepo getPaoRepo() {
return new PaoRepo(
DaggerAppComponent.this.providePaoServiceProvider.get(),
DaggerAppComponent.this.providePaoDaoProvider.get());
}
private PaoViewModel getPaoViewModel() {
return new PaoViewModel(getPaoRepo());
}
@Override
public void inject(PaoActivity arg0) {
injectPaoActivity(arg0);
}
private PaoActivity injectPaoActivity(PaoActivity instance) {
PaoActivity_MembersInjector.injectMViewModel(instance, getPaoViewModel());
return instance;
}
}
}
到此,经过分析Dagger-Android的注入过程,我们了解了他们的工作原理。
最后总结归纳一下:
- 普通的赋值:viewmodel = ViewModel(Repo())
- Dagger的注入: instance.viewmodel = component.viewmodel
- Dagger-Android的注入:instance.viewmodel = map.get(instance.class).getComponent().viewmodel
就一个思路的转变,google的大神真是有心了,搞这么多事。
写在最后
Dagger-Android相比于普通的Dagger确实稍微绕了一些,多了一些设计模式和面向接口,光看源码的话很容易绕晕,特别是在不懂得google大神们的思路的时候。
如果说Dagger的复杂度是5,那么Dagger-Android的复杂程度就是7。
如果能明悟的话,逻辑也是很简单的,然后多进行断点调试。就像解算法题一样,Dagger和Dagger-Android可以算是两种思路吧。
如同写在前面的话里提到的,Dagger适合于那些初中级开发者的团队,比较容易理解。Dagger-Android则更适合实力都较强的开发团队。)
github:https://github.com/ditclear/MVVM-Android/tree/dagger-android
参考文档:
Dagger-Android
告别Dagger2模板代码:DaggerAndroid原理解析 (推荐)