前言
当前基于2.38.1源码整理出来的一篇实际应用;
现在回头看这篇文章,其实发现内容其实真的不错(对几年前自己啃源码的认可),但是之前的文章的结构性确实有点“呵呵”,反正我回头看了才发现其中主要问题:①文章没有整体思路;②不够通俗易懂;
所以有了现在对dagger2实际应用的翻新之作,意在能让人更好的理解dagger2。
分三部分讲解:dagger2 、dagger2-android、 hilt
下面从dagger2主体到细节,并且运用实际案例逐一讲解各个注解的实际应用。
以下叙述的案例主体来源于aac的GithubBrawserSample,可自行下载查看。
一 依赖注入-DI
DI-Dependency Injection。
在java中我们正常使用一个对象:
A a = new A();
通过依赖注入:
(1)首先创建一个容器C对象,这个容器C对象生命周期和项目绑定-C容器随着项目的创建而创建,随着项目的销毁而销毁;容器中创建一个A对象:
public class C{
A a;
public static C c;
public static newInstance(){
if(c = null){ c = new C();}
return c;
}
public void instance(){
a = new A();
}
public A getA(){
return a;
}
}
我们可以在MyApplication的oncreate方法中通过C.newInstance()实例化C对象。
(2)当我们要使用A对象时,直接在C对象中获取到实例化的A即可,如下B对象要使用A对象了
public class B{
private A a = MyApplication.getC().getA();
...
}
这个就是一套依赖注入的思想。是不是感觉有点烧脑,我特么好好得new对象不香吗,这么麻烦。
确实。很多时候没必要这么去做,并不是每个项目都需要通过依赖注入去做的,而且实现也比较麻烦,也不容易懂。项目开发成本随之提高,尤其现在国内对Dagger2还是比较排斥的,用的不多。
那么为什么要使用依赖注入?
使用依赖注入原因:
- **官方背景。**依赖注入是google推出来的,和aac一起推出来的;
- **已得到广泛应用。**依赖注入思想其实在
spring架构中已经被使用很久了(如果你们了解spring源码的话); - **(重点)易于结构层维护。**依赖注入更注重于架构方面的调整:是为了提高开发效率、易解耦。
对一个简单的项目,或者项目成员整体水平不高的情况下,依赖注入不但不会带来实质性帮助,反而起到束缚作用。但是对于一个大型项目来说,依赖注入的思想起到非常至关重要的作用,典型的就是插拔。
什么叫插拔?
嗯,用了一个框架(比如glide),感觉不符合当前项目需求了(想换一个图片加载机制),我要换一个,这个时候就体现依赖注入的重要性和方便性。
android实现依赖注入最好的办法就是Dagger2架构。
二 dagger2
(一)主体案例
AppComponent.kt
@Singleton
@Component(
modules = [//modules节点的作用就是往容器中实例化对象
...,
AppModule::class]
)
interface AppComponent {
@Component.Builder
interface Builder {
//传递参数
@BindsInstance
fun application(application: Application): Builder
//实例化AppComponent对象
fun build(): AppComponent
}
//将当前Appcomponent容器注入GithubApp中
fun inject(githubApp: GithubApp)
}
调用DaggerAppComponent.builder().application(githubApp).build().inject(githubApp)表示容器注入到GithubApp中。
AppModule.kt
@Module(includes = [ViewModelModule::class])//includes表示子module节点,同样表示往容器中实例化对象
class AppModule {
@Singleton
@Provides
fun provideGithubService(): GithubService {//实例化GithubService 对象
return Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.build()
.create(GithubService::class.java)
}
@Singleton
@Provides
fun provideDb(app: Application): GithubDb {//实例化GithubDb 对象,当前方法参数来源于AppComponent 类的Builder类application方法
return Room
.databaseBuilder(app, GithubDb::class.java, "github.db")
.fallbackToDestructiveMigration()
.build()
}
@Singleton
@Provides
fun provideUserDao(db: GithubDb): UserDao {//实例化UserDao 对象
return db.userDao()
}
@Singleton
@Provides
fun provideRepoDao(db: GithubDb): RepoDao {//实例化RepoDao 对象
return db.repoDao()
}
}
AppModule类中实例化UserDao 和RepoDao,需要传递GithubDb参数:当前类中的provideDb方法提供了GithubDb参数,那么问题来了,我们如何确保GithubDb的实例化在UserDao 和RepoDao 实例化的前面?
其实Dagger源码在容器中实例化对象之前,会做一个排列。某某对象实例化需要引用到另外对象,做一个顺序排列,Dagger会很好的掌握对象实例化顺序。
AppModule.kt还包含一个子module节点
ViewModelModule.kt
@Suppress("unused")
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(UserViewModel::class)
abstract fun bindUserViewModel(userViewModel: UserViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(SearchViewModel::class)
abstract fun bindSearchViewModel(searchViewModel: SearchViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RepoViewModel::class)
abstract fun bindRepoViewModel(repoViewModel: RepoViewModel): ViewModel
@Binds
abstract fun bindViewModelFactory(factory: GithubViewModelFactory): ViewModelProvider.Factory
}
这个类主要是通过GithubViewModelFactory实例化UserViewModel、SearchViewModel和RepoViewModel对象;
bindUserViewModel方法参数通过class UserViewModel @Inject constructor(userRepository: UserRepository, repoRepository: RepoRepository) :Inject注解修饰构造函数实例化;
也就是说到目前为止,实例化对象可以通过
module节点的方法,还可以通过Inject修饰构造函数的方式。
(二)核心注解
这里主要针对:
-
(sub)component节点被这些注解修饰:@Component、@ProductionComponent、@Subcomponent、@ProductionSubcomponent; -
creator节点被这些注解修饰:@Component.Factory、@ProductionComponent.Factory、@Subcomponent.Factory、@ProductionSubcomponent.Factory、@Component.Builder、@ProductionComponent.Builder、@Subcomponent.Builder、@ProductionSubcomponent.Builder;
creator节点有两种模式:Factory和Builder,分别表示生成(sub)component容器类采用工厂模式和构建者模式;
-
module节点被这些注解修饰:@Module、@ProducerModule; -
bindingMethod节点被这些注解修饰:@Provides、@Produces、@Binds、@Multibinds、@BindsOptionalOf、@BindsInstance; -
针对
@Inject、@AssistedInject、@Assisted、@AssistedFactory注解解说。@AssistedInject、@Assisted和@AssistedFactory三个是Dagger新出现的注解,在一起使用。@Inject主要是用于修饰构造函数、变量和普通方法。
所有使用
Dagger注解,建议都不要使用private修饰,最好使用public修饰,这是铁律。
component节点
component节点表示使用@Component、@ProductionComponent修饰的节点。
subcomponent节点表示使用@Subcomponent或@ProductionSubcomponent修饰的节点。
(sub)component节点注解仅仅用于修饰类或接口,具体规则如下:
-
(sub)component节点上有且仅有一个@Component、@ProductionComponent、@Subcomponent或@ProductionSubcomponent注解修饰; -
如果
(sub)component节点上使用了@CancellationPolicy注解修饰,那么当前(sub)component节点只能使用@ProductionSubcomponent或@ProductionComponent注解; -
(sub)component节点只能是abstract抽象类或接口; -
(sub)component节点最多只能存在一个creator节点; -
(sub)component节点不能使用@Reusable修饰; -
如果
(sub)component是一个kotlin文件,那么(sub)component节点中的componentMethod方法名不能使用java关键字; -
(sub)component使用的注解的dependencies方法不允许收集module节点:
@Component#dependencies(module.class)错误;
componentMethod方法
componentMethod表示(sub)component节点中的方法,仅仅针对abstract修饰(或接口方法)、非private、非static的(包括从父级类继承过来的)componentMethod方法做校验,规则如下:
-
componentMethod方法返回类型如果是subcomponent或subcomponent.creator,该方法最多只能出现一次。返回类型是subcomponent和subcomponent.creator不允许同时出现在一个(sub)component节点中; -
componentMethod方法不能使用泛型,如果当前(sub)component节点是kotlin文件,那么注意componentMethod不能使用java关键字; -
如果
componentMethod方法的returnType返回类型是subcomponent节点:
-
(1)当前
componentMethod方法的参数必须有并且是module节点(当前方法参数中的module节点类型只允许出现一次),并且这个module节点来源于(2)-subcomponent关联的module节点; -
(2)收集这个
subcomponent节点关联的module节点:①subcomponentAnnotation#modules里面的module节点;②条件①module节点上的注解moduleAnnotation#includes里面的module节点;③条件①和条件②的module节点的父级module(使用了moduleAnnotation注解

本文基于源码详细介绍了Dagger2在Android中的实际应用,包括依赖注入的概念、Dagger2的核心注解、组件及其实例化顺序、模块绑定方法等内容,旨在帮助读者更好地理解和使用Dagger2。

4027

被折叠的 条评论
为什么被折叠?



