dagger2相关
dagger2笔记:
是个依赖注入框架。Dagger2是第一个用生成的代码实现完整堆栈的。指导原则是生成代码,模仿用户可能已经手写的代码,以确保依赖注入尽可能简单、可跟踪和高性能。
1 | public class MainActivity extends AppCompatActivity implements MainContract.View { |
1 | public class MainPresenter { |
说明:
@Module
内@privides
提供 –>提供给没有构造函数的类的依赖,这些类无法用@Inject
标注(比如第三方库、系统类、以及MVP
中Contract
接口)
桥梁:@Component
接口(或抽象)类上,说明modules
有哪些
用@Inject
声明的类成员属性不能用private
修饰(MainActivity
中MainPresenter
类对象用@Inject
修饰)
某个类被@Inject
标记时会自动到它的构造方法中,如果这个构造方法也被@Inject
标记的话,就会自动初始化这个类,从而完成依赖注入。(当然他们之间需要个@Component
当桥梁产生联系)
一个Component
类可以包含多个Module
类,用来提供依赖
注入过程:
首先MainActivity
需要依赖MainPresenter
,因此,我们在里面用@Injec
t对MainPresenter
进行标注,表明这是要注入的类。然后,我们对MainPresenter
的构造函数也添加注解@Inject
,此时构造函数里有一个参数MainContract.View
,因为MainPresenter
需要依赖MainContract.View
,所以我们定义了一个类,叫做MainModule
,提供一个方法provideMainView
,用来提供这个依赖,这个MainView
是通过MainModule
的构造函数注入进来的,接着我们需要定义Component
接口类,并将Module
包含进来,即
1 |
|
同时里面声明了一个inject
方法,方法参数为ManActivity
,用来获取MainActivity
实例,以初始化在里面声明的MainPresenter
1 | DaggerMainComponent.builder() |
@Inject
带有此注解的属性或构造方法将参与到依赖注入中,Dagger2
会实例化有此注解的类@Module
带有此注解的类,用来提供依赖,里面定义一些用@Provides
注解的以provide
开头的方法,这些方法就是所提供的依赖,Dagger2
会在该类中寻找实例化某个类所需要的依赖。@Component
用来将@Inject
和@Module
联系起来的桥梁,从@Module
中获取依赖并将依赖注入给@Inject
Dagger2注入原理
通过apt
插件在编译阶段生成相应的注入代码
MainPresenter
实例化过程,MainPresenter
会有一个工厂类,在这个类的get()
中进行MainPresenter
创建,而MainPresenter
所需要的View
依赖,是由MainModule
里定义的以provide
开头方法所对应的工厂类提供的。
Dagger2基础知识及在Java中使用(1)
比喻:
@Component
:相当于个注射器(是接口);@Module
相当于注射液,就是数据源(是类或抽象类);
要把注射液放入指定哪个注射器如:@Component(modules=...)
;@Inject
相当于标注被注射体。
首先添加依赖
1 | implementation 'com.google.dagger:dagger:2.24' |
1. @Inject
& @Component
可以简单实现依赖注入(不带@Module)
1 | //无参构造函数的类 |
1 | //定义我们的Component |
此时,点下AS》build》Make Project,让apt帮我们生成代码。一般生成代码为Dagger+你定义Component类名。
1 | //在我们的Activity中 |
2. 带@Module的使用
无参构造函数的情况
假设Human类是第三方类库,里面没有@Inject
,那么我们使用@Module
1 | //假设这个类是第三方类库中的,里面没有@Inject |
1 | //定义我们的数据源Module |
1 | //定义我们的Component。带上了参数 |
- 改不了的第三方库等类对象要注入到指定位置则需要用到
@Module
+@Component
+@Inject
。而且**@Module
标记的对象的优先级比@Inject
标记的高** - 通过
Module
传参
1 |
|
providesWoman
用@Provides
标注,它会先去找Soul
的初始化,先通过@Provides
找Soul
,找到了providesSoul
。假如此时没有providesSoul
,则会去Soul
类中找有没有被@Inject
标记的构造方法,如果还没有,那么报错。
注意:
- 在
Module
的构造函数带有参数且参数被使用的情况下,所生产的Component
类就没有create()
方法了 - 在这里的
module
如果没有providesSoul()
方法的话,还有一种情况只要在Soul
的构造方法有@Inject
也是可以的。
自己在
Component
中写@Component.Builder
,并在其标记的接口内加@BindInstance
,可以避免在DaggerXXXComponent.builder().xxx
调用的时候再创建对象,直接传参即可Componet
依赖Component
,使用dependence
1
2
3FragmentComponent依赖于ActivityComponent
FragmentComponent中 injectTo 在Activity中实例化并提供对外的activityComponent对象
在Fragment中通过DaggerFragmentComponent.builder().activityComponent(ActivityComponent对象).build().injectTo(this);使用
@SubComponent
跟使用dependence
效果一样1
2
3
4ActivityComponent中获取FragmentComponent对象
FragmentComponent标记@Subcomponent,并加injectTo
在Activity中实例化并提供对外的activityComponent对象
在Fragment中通过ActivityComponent.fragmentComponent().injectTo(this);使用
@SubComponent
和@Subcomponent.Builder
,效果与5、6一样1
2
3
4ActivityComponent中获取FragmentComponent.Builder对象
FragmentComponent标记 ,加injectTo,并加 .Builder里面只有个build()方法
在Activity中实例化并提供对外的activityComponent对象
在Fragment中通过ActivityComponent.sonbuilder().build().injectTo(this);
Dagger2基础知识及在Java中使用(2)
Dagger2在Android中的使用
例子:
1 | implementation 'com.google.dagger:dagger-android:2.24' |
1 | public class Children { |
1 | public class SurperMan { |
1 |
|
dagger.android包提供了一个@ContributesAndroidInjector标注是为了省去我们每次去Activity里初始化Component
1 |
|
1 | //这里AndroidSupportInjectionModule是系统的,必须加上 |
1 | public class MyApplication extends DaggerApplication { |
在Activity里使用或者在Fragment里使用,只需继承我们的DaggerActivity&DaggerFragment。直接看我们的Activity
1 | public class ForAndroidActivity extends DaggerAppCompatActivity{ |
Dagger2华丽使用在MVP框架中
RxJava + Retrofit + MVP + Dagger2
1 | //引入dagger.android库 |
1 |
|
1 |
|
初始化Presenter:
无参可以直接用@Inject,若有参数则要用Module
1 | public class PostPresenter extends BasePresenter<PostContract.View> implements PostContract.Prensenter { |
Application中:
1 | public class MyApplication extends DaggerApplication { |
因为我们的Activity要继承DaggerActivity、DaggerFragment,所以我们新建BaseDaggerActivity和BaseDaggerFragment,里面有DaggerActivity和RxActivity的一些内容:
1 | public abstract class BaseDaggerActivity<T extends BasePresenter> extends RxFragmentActivity implements BaseView, HasAndroidInjector { |
各注解用法
Dagger2各属性了解
- 必要属性
@inject
//注入 , @Component
, @Module
, @Provider
要用Dagger2这几个属性是绕不开的
- 高级属性
@Named
, @Qualifier
, @Scope
, @Singleton
可分两组,@Named
底层实现是@Qualifier
,@Singleton
底层实现是@Scope
Dagger2各属性使用及分析
@Inject
注解构造函数:
通过标记构造函数,告诉Dagger2可以创建该类的实例(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖关系。
若注解了构造函数,没有注解依赖变量,将不创建实例。
用@Inject注解应该用来创建类实例的构造函数
注解依赖变量:
通过标记依赖变量,Dagger2提供依赖关系,注入变量。
若注解了依赖变量,而没注解构造函数,将获得一个null对象。
用@Inject注解构造函数,再用@Inject注解类实例
@Module
通过注解类,为Dagger提供依赖关系
@Provides
是一种工厂模式
@Provides
依然是为了提供依赖关系,是对@Inject
的补充,弥补了@Inject
对于第三方框架和接口类的盲区。
@Provides
方法本身可能也拥有自身的依赖关系。
@Provides
方法本身是不能独立存在的,它必须依附于一个Module
1 | public class LstudyActivity extends BaseActivity { |
不调用get()方法的Provider的HashCode值。一直不变
调用get()方法的Provider的HashCode值。一直在改变
调用get()方法的Lazy的HashCode值。一直不变
provider是一种工厂模式,每次调用get(),都会生成不同的实例。不调用不会生成实例(返回的是容器的HashCode)。
@Component
通过注解接口类,作为提供依赖和所需依赖之间的桥梁(链接Module
和Container
),在编译时生成相关实例,将相关依赖注入完成使命。
依赖规则
若@Inject
和@Module
都提供了依赖对象
- 查找Module中是否存在创建该类的方法
- 若存在,初始化参数,完成实例创建
- 若不存在,用
@Inject
创建实例
@Singleton
是@Scope的一种。是作用域单例。Component在哪build就跟那个类的生命周期绑定,即只在那个绑定的实例类里起作用
1
2
3
4 //注明是Scope
//标记文档提示
//运行时候级别
public Singleton {}1、Component在Activity里build,那么这个单例只限于在这个Activity里。意思退出这个Activity,再进这个Activity时,这个单例会被重新new
2、如果Component在Application里build,那么这个单例就是全局的。注意这里涉及到了我们之前的Component依赖Component。
使用Dagger2要有局部单例和全局单例的意识。(变量地址是否相同)
两者的基本定义是一样的,要看Component的作用域,若Component的作用域是全局的,那么对应@Singleton
或者@Scope
的对应module就全局的,反之亦然。
1 |
|
局部单例:Component传入具体的某个子类Activity
1 |
|
全局单例:步骤:
- Component传入基类的Activity
1 |
|
- 在Application中实例化它
利用率Application的唯一特性,也可以另起一个全局唯一的类承载,但没必要
1 | public class MyApplication extends Application{ |
- Client中调用
在基类中进程初始化,然后加标记进行控制,对子类暴露,这样子类想要的时候可以直接拿,更重要的是可以避免Component中inject(XXXActivity)新增类的问题
1 | public abstract class BaseActivity extends NetObserverActivity{ |
@Scope
- Scope是Singleton的底层实现,一般在工程实施中,它作为提高可读性的手段之一让程序员可以一眼就看出哪些是全局哪些是局部的生命周期
- 来标识范围的注解,并控制如何重复使用类的实例。仅用@inject注解,未用@Scope注解,在每次依赖注入的时候都会创建新的实例。使用@Scope注解会缓存第一次创建的实例,然后重复注入缓存的实例,不会创建新的实例。
- 如果有类注入实例的类被@Scope注解,那么其Component必须被相同的Scope注解。
- 如果依赖实例的注入来源是@Provides方法时,@Provides方法必须被@Scope注解;
- 如果依赖实例的注入来源是@Inject注解的构造函数时,实例类必须被@Scope注解。@Scope实际上是对注入器的控制。
另外在看其它博客文章的时候,你会经常看到 @ActivityScope 可以声明一个Activity生命周期的对象 ,@ApplicationScope 可以声明一个Application生命周期的对象 , 难道这些Scope这么神? 定义一个名字就可以控制对象的生命周期了? 其实这和 @Singleton一样的,都是代码是否通过同一个 component 对象来控制的。比如 @ActivityScope 定义的对象 ,其在Activity创建了component对象 ,那这个component对象肯定在这个Activity的生命周期内啊,依赖创建出来的对象也肯定是这个Activity啊。还有@ApplicationScope 中的component 对象是在 Application中的,那依赖创建出来的对象的生命周期也肯定是和 @Singleton的一样的,单例的生命周期不就是整个 Application 吗。
@Qualifier
限定符,它可以标记不同的构造方法让外部调用者获取期望的构造对象
1 | /** |
如果把Type改成Named那么就跟@Named一样了
首先自定义一个注解@interface
,刨开2个元注解不看 @Documented @Retention,顶部定义一个 @Qualifier
限定符标识,然后这一句String value() default “”的意思是,Type注解可以传参,默认为一个空字符串。
定义好了这个注解就相当于定义好了一个可接收参数的标签,接下来我们要将这个标签传入不同的参数然后分别贴到不同的构造函数之上(记住其中的关系,**@Provide + @Type(“”)区分不同构造函数,Type又被@Qualifier修饰**),这样,就相当于让每个构造函数有了辨识的途径,若要使用不同的构造函数,则只需去揭开不同的标签即可。
1 | /** |
Module传入不同参数分别标记不同的构造方法。
1 | /** |
Component通过刚才自定义的@Type注解标注对外暴露的接口,当这个接口方法被调用时,它会去Moudle内找对应的相同的@Type参数列表一致的构造与provider方法。
@Named
Named底层实现是@Qualifier,是@Qualifier的一种
源码
1 |
|
简介 原文链接
Dagger2是个依赖注入框架
依赖注入:是种实现控制反转用于解决依赖性设计模式。一个依赖关系指的是可被利用的一种对象(即服务提供端)。依赖注入是将所依赖的传递给将使用的从属对象(即客户端)。该服务是将会变成客户端的状态的一部分。传递服务给客户端,而非允许客户端来建立或寻找服务,是本设计模式的基本要求。–》简单说就是将实例对象传入到另一个对象中去(给一个对象传入一个实例变量)。
依赖注入的实现
- 构造函数注入
1 | public class Chef{ |
- setter方法注入
1 | public class Chef{ |
- 接口注入
1 | public interface MenuInject{ |
- 依赖注入框架
1 | public class Menu{ |
Dagger2 实现依赖注入
Dagger2(2.17之后的版本)最简单的使用方式就是下面这种:
1 | public class A{ |
假如,A还需要依赖其他的类,且这个类是第三方库中提供的。又或者A实现了C接口,我们就需要用依赖倒置原则来加强我们的代码的可维护性等等。
例子:做个餐饮系统,把点好的菜单发给厨师,让厨师负责做菜。
1 | //引入一些Dagger For Android的依赖库 |
Cooking接口
1 | public interface Cooking{ |
Chef
1 | public class Chef implements Cooking{ |
Menu
1 | public class Menu { |
现在我们写一个Activity,作用是在onCreate方法中使用Chef对象实现cooking操作。我们先来看看不使用Dagger2和使用Dagger2的代码区别。
MainActivity
1 | public class MainActivity extends Activity { |
DaggerMainActivity
1 | public class DaggerMainActivity extends DaggerActivity { |
在DaggerMainActivity上实现依赖注入还需要哪些代码。
CookModules
1 |
|
ActivityModules
1 |
|
CookAppComponent
1 |
|
MyApplication
1 | public class MyApplication extends DaggerApplication{ |
Dagger2 For Android 使用要点分析
- CookModules
CookModule很简单,它的目的就是通过@Providers注解提供Menu对象需要的数据。因为Menu是需要依赖一个Map对象的,所以我们通过CookModules给它构造一个Map对象,并自动把它注入到Menu实例里面。 - ActivityModules
ActivityModules的主要作用就是通过@ContributesAndroidInjector来标记哪个类需要使用依赖注入功能,这里标记的是ManActivity,所以MainActivity能通过@Inject注解来注入Chef对象。 - CookAppComponent
CookAppComponent相当于一个注射器,我们前面定义的Modules就是被注射的类,使用@Inject注入对象的地方就是接收者类。 - MyApplication
MyAppliction的特点是继承了DaggerAppliction类,并且在applicationInjector方法中构建了一个DaggerCookAppComponent注射器。
这就是Dagger 2在Android中的使用方案了,在这里我们可以看到,接收这类(MainActivity)中的代码非常简单,实现依赖注入只使用了:
1 |
|
在接收类里面完全没有多余的代码,如果我们要拓展可以SecondsActivity的话,在SecondsActivity我们要用到Menu类。
那么我们只需要在ActivityModules中增加:
1 |
|
然后在SecondsActivity注入Menu:
1 | @Inject |
可以看到,对于整个工程来说,实现使用Dagger2 For Android实现依赖注入要写的模版代码其实非常少,非常简洁。只需要进行一次配置就可以,不需要频繁写一堆模版代码。总的来说,Dagger2造成模版代码增加这个问题已经解决了。
Dagger2的优势
在这里我们总结下使用Dagger2带来的优点。
- 减少代码量,提高工作效率
例如上面的例子中,我们构建一个Chef对象的话,不使用Dagger2的情况下,需要在初始化Chef对象之前进行一堆前置对象(Menu、Map)的初始化,并且需要手工注入到对应的实例中。你想像下,如果我们再加一个Restaurant( 餐馆 )对象,并且需要把Chef注入到Restaurant中的话,那么初始化Restaurant对象时,需要的前置步骤就更繁琐了。
可能有人会觉得,这也没什么啊,我不介意手工初始化。但是如果你的系统中有N处需要初始化Restaurant对象的地方呢?使用Dagger2 的话,只需要用注解注入就可以了。 - 自动处理依赖关系
使用Dagger2的时候,我们不需要指定对象的依赖关系,Dagger2会自动帮我们处理依赖关系(例如Chef需要依赖Menu,Menu需要依赖Map,Dagger自动处理了这个依赖关系)。 - 采用静态编译,不影响运行效率
因为Dagger2是在编译期处理依赖注入的,所以不会影响运行效率在一定的程度上还能提高系统的运行效率(例如采用Dagger2实现单例,不用加锁效率更高)。 - 提高多人编程效率
在多人协作的时候,一个人用Dagger2边写完代码后,其它所有组员都能通过@Inject注解直接注入常用的对象。加快编程效率,并且能大大增加代码的复用性。
- @ContributesAndroidInjector
你可以把它看成Dagger2是否要自动把需要的用到的Modules注入到DishesFragment中。这个注解是Dagger2 For Android简化代码的关键 - @Module
被这个注解标记的类可以看作为依赖对象的提供者,可以通过这个被标记的类结合其它注解来实现依赖关系的关联。 - @Provides
主要作用就是用来提供一些第三方类库的对象或提供一些构建非常复杂的对象在Dagger2中类似工厂类的一个角色。 - @Binds
主要作用就是确定接口与具体的具体实现类。
Dagger2 For Android是如何注入依赖的?
我们在用Dagger2的时候是通过一些模版代码来实现依赖注入的( DaggerXXXComponent.builder().inject(xxx) 这种模版代码),但是在Demo中的DishesFragment根本没看到类似的代码啊,那么这些对象是什么时候注入到DishesFragment重的呢?
答案就是**@ContributesAndroidInjector**注解
我们先来看看Dagger2是通过什么方式来实现自动把依赖注入到DishesActivity中的。
ActivityModules
1 |
|
没错,就是@ContributesAndroidInjector这个注解,modules就代表这个DishesActivity需要依赖哪个Modules。
Dagger2 For Android使用要点
我们现在来总结下,简化版的Dagger实现依赖注入的几个必要条件:
- 第三方库通过Modules的@provides注解来提供依赖
- 提供一个全局唯一的Component,并且Modules中需要添加AndroidSupportInjectionModule类,它的作用时关联需求与依赖之间的关系
- Application需要继承DaggerApplication类,并且在applicationInjector构建并返回全剧唯一的Component实例
- 其它需要使用依赖注入的组建都需要继承Dagger组件名字类,并且需要在相应的Modules中通过@ContributesAndroidInjector注解标记需要注入依赖的组建。