Dagger2实际应用篇

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

前言

当前基于2.38.1源码整理出来的一篇实际应用;

现在回头看这篇文章,其实发现内容其实真的不错(对几年前自己啃源码的认可),但是之前的文章的结构性确实有点“呵呵”,反正我回头看了才发现其中主要问题:①文章没有整体思路;②不够通俗易懂;

所以有了现在对dagger2实际应用的翻新之作,意在能让人更好的理解dagger2

分三部分讲解:dagger2dagger2-androidhilt

下面从dagger2主体到细节,并且运用实际案例逐一讲解各个注解的实际应用。

以下叙述的案例主体来源于aacGithubBrawserSample,可自行下载查看。

一 依赖注入-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还是比较排斥的,用的不多。

那么为什么要使用依赖注入?

使用依赖注入原因:

  1. **官方背景。**依赖注入是google推出来的,和aac一起推出来的;
  2. **已得到广泛应用。**依赖注入思想其实在spring架构中已经被使用很久了(如果你们了解spring源码的话);
  3. **(重点)易于结构层维护。**依赖注入更注重于架构方面的调整:是为了提高开发效率、易解耦。

对一个简单的项目,或者项目成员整体水平不高的情况下,依赖注入不但不会带来实质性帮助,反而起到束缚作用。但是对于一个大型项目来说,依赖注入的思想起到非常至关重要的作用,典型的就是插拔

什么叫插拔?

嗯,用了一个框架(比如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修饰构造函数的方式。

(二)核心注解

这里主要针对:

  1. (sub)component节点被这些注解修饰:@Component@ProductionComponent@Subcomponent@ProductionSubcomponent;

  2. creator节点被这些注解修饰:@Component.Factory@ProductionComponent.Factory@Subcomponent.Factory@ProductionSubcomponent.Factory@Component.Builder@ProductionComponent.Builder@Subcomponent.Builder@ProductionSubcomponent.Builder

  • creator节点有两种模式:FactoryBuilder,分别表示生成(sub)component容器类采用工厂模式和构建者模式;
  1. module节点被这些注解修饰:@Module@ProducerModule

  2. bindingMethod节点被这些注解修饰:@Provides@Produces@Binds@Multibinds@BindsOptionalOf@BindsInstance;

  3. 针对@Inject@AssistedInject@Assisted@AssistedFactory注解解说。@AssistedInject、@Assisted和@AssistedFactory三个是Dagger新出现的注解,在一起使用。@Inject主要是用于修饰构造函数、变量和普通方法。

所有使用Dagger注解,建议都不要使用private修饰,最好使用public修饰,这是铁律。

component节点

component节点表示使用@Component@ProductionComponent修饰的节点。

subcomponent节点表示使用@Subcomponent@ProductionSubcomponent修饰的节点。

(sub)component节点注解仅仅用于修饰类或接口,具体规则如下:

  1. (sub)component节点上有且仅有一个@Component@ProductionComponent@Subcomponent@ProductionSubcomponent注解修饰;

  2. 如果(sub)component节点上使用了@CancellationPolicy注解修饰,那么当前(sub)component节点只能使用@ProductionSubcomponent@ProductionComponent注解;

  3. (sub)component节点只能是abstract抽象类或接口;

  4. (sub)component节点最多只能存在一个creator节点;

  5. (sub)component节点不能使用@Reusable修饰;

  6. 如果(sub)component是一个kotlin文件,那么(sub)component节点中的componentMethod方法名不能使用java关键字;

  7. (sub)component使用的注解的dependencies方法不允许收集module节点:

  • @Component#dependencies(module.class)错误;

componentMethod方法

componentMethod表示(sub)component节点中的方法,仅仅针对abstract修饰(或接口方法)、非private、非static的(包括从父级类继承过来的)componentMethod方法做校验,规则如下:

  1. componentMethod方法返回类型如果是subcomponentsubcomponent.creator,该方法最多只能出现一次。返回类型是subcomponentsubcomponent.creator不允许同时出现在一个(sub)component节点中;

  2. componentMethod方法不能使用泛型,如果当前(sub)component节点是kotlin文件,那么注意componentMethod不能使用java关键字;

  3. 如果componentMethod方法的returnType返回类型是subcomponent节点:

  • (1)当前componentMethod方法的参数必须有并且是module节点(当前方法参数中的module节点类型只允许出现一次),并且这个module节点来源于(2)-subcomponent关联的module节点;

  • (2)收集这个subcomponent节点关联的module节点:①subcomponentAnnotation#modules里面的module节点;②条件①module节点上的注解moduleAnnotation#includes里面的module节点;③条件①和条件②的module节点的父级module(使用了moduleAnnotation注解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值