依赖注入利器Dagger.docx

上传人:b****5 文档编号:5399471 上传时间:2022-12-15 格式:DOCX 页数:21 大小:278.14KB
下载 相关 举报
依赖注入利器Dagger.docx_第1页
第1页 / 共21页
依赖注入利器Dagger.docx_第2页
第2页 / 共21页
依赖注入利器Dagger.docx_第3页
第3页 / 共21页
依赖注入利器Dagger.docx_第4页
第4页 / 共21页
依赖注入利器Dagger.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

依赖注入利器Dagger.docx

《依赖注入利器Dagger.docx》由会员分享,可在线阅读,更多相关《依赖注入利器Dagger.docx(21页珍藏版)》请在冰豆网上搜索。

依赖注入利器Dagger.docx

依赖注入利器Dagger

依赖注入利器-Dagger‡

概述

在开发过程中,为了实现解耦,我们经常使用依赖注入,常见的依赖注入方式有:

构造方法注入:

在构造方法中把依赖作为参数传递进去

setter方法注入:

添加setter方法,把依赖传递进去

接口注入:

把注入方法抽到一个接口中,然后实现该接口,把依赖传递进去

下面用一个小栗子来说明三种方式的用法:

publicclassPersonServiceimplementsDependencyInjecter{

privatePersonDaopersonDao;

//构造方法注入

publicPersonService(PersonDaopersonDao){

this.personDao=personDao;

}

//setter方法注入

publicvoidsetPersonDao(PersonDaopersonDao){

this.personDao=personDao;

}

//接口注入:

实现DependencyInjecter接口

@Override

publicvoidinjectPersonDao(PersonDaopersonDao){

this.personDao=personDao;

}

......

}

我们来看下使用一般的依赖注入方法时,代码会是怎么样的:

publicclassMainActivityextendsAppCompatActivity{

privatePersonServicemService;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//创建PersonService的依赖:

personDao

PersonDaopersonDao=newPersonDaoImpl();

//通过构造方法注入依赖

mService=newPersonService(personDao);

}

}

看起来还好是吧?

但现实情况下,依赖情况往往是比较复杂的,比如很可能我们的依赖关系如下图:

PersonDaoImpl依赖类A,类A依赖B,B依赖C和D…在这种情况下,我们就要写出下面这样的代码了:

publicclassMainActivityextendsAppCompatActivity{

privatePersonServicemService;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//创建依赖D

Dd=newD();

//创建依赖C

Cc=newC();

//创建依赖B

Bb=newB(c,d);

//创建依赖A

Aa=newA(b);

//创建PersonService的依赖:

personDao

PersonDaopersonDao=newPersonDaoImpl(a);

//通过构造方法注入依赖

mService=newPersonService(personDao);

}

}

MainActivity只是想使用PersonService而已,却不得不关注PersonService的依赖是什么、PersonDaoImpl依赖的依赖是什么,需要把整个依赖关系搞清楚才能使用PersonService。

而且还有一个不好的地方,一旦依赖关系变更了,比如A不再依赖B了,那么就得修改所有创建A的地方。

那么,有没有更好的方式呢?

Dagger就是为此而生的,让我们看看使用Dagger后,MainActivity会变成什么模样:

publicclassMainActivityextendsAppCompatActivity{

@Inject

PersonServicemService;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Dagger注入,读者现在可先不关注里面做了什么操作

DaggerPersonServiceComponent.create().inject(MainActivity.this);

//注意,mService已经是非空了,可以正常使用

mService.update(1,"HansChen");

......

}

}

之前创建A、B、C、D、PersonDaoImpl等依赖的代码全不见了,只需要调用一个注入语句就全搞定了。

调用了注入语句之后,mService就可以正常使用了,是不是挺方便呢?

至于这句注入语句具体干了什么,读者现在可以先不管,后面会有详细说明,这里只是做一个使用演示而已。

我们大概猜想一下,在MainActivity使用PersonService需要做哪些?

分析生成依赖关系图,如PersonService–>PersonDaoImpl–>A–>B–>C&D

根据依赖关系图获取相关依赖,比如依次创建D、C、B、A、PersonDaoImpl、PersonService的实例

把生成的PersonService实例传递给MainActivity的mService成员变量

其实Dagger做的也就是上面这些事情了,接下来就让我们真正开始学习Dagger吧

声明需要注入的对象

首先我们应该用javax.inject.Inject去注解需要被自动注入的对象,@Inject是Java标准的依赖注入(JSR-330)注解。

比如下面栗子中,需要注入的对象就是MainActivity的mService。

这里有个要注意的地方,被@Inject注解的变量不能用private修饰

publicclassMainActivityextendsAppCompatActivity{

//注意,不能被private修饰

@Inject

PersonServicemService;

......

}

如何实例化出依赖?

在执行依赖注入的时候,Dagger会查找@Inject注解的成员变量,并尝试获取该类的实例,Dagger最直接的方式就是直接new出相应的对象了。

实例化对象的时候,会调用对象的构造方法,但假如有多个构造方法,具体用哪个构造方法来实例化对象?

Dagger肯定是不会帮我们“擅自做主”的,用哪个构造方法来实例化对象应该是由我们做主的,所以我们需要给相应的构造方法添加@Inject注解。

当Dagger需要实例化该对象的时候,会调用@Inject注解的构造方法来实例化对象:

publicclassPersonServiceimplementsDependencyInjecter{

privatePersonDaopersonDao;

//用@Inject注解,相当于告诉Dagger需要实例化PersonService的时候,请调用这个构造方法

@Inject

publicPersonService(PersonDaopersonDao){

this.personDao=personDao;

}

......

}

聪明的你应该发现了,调用PersonService的构造方法需要传入PersonDao实例,所以要实例化PersonService,必须先要实例化PersonDao,Dagger会帮我们自动分析出这个依赖关系,并把它添加到依赖

关系图里面!

Dagger会尝试先去实例化一个PersonDao,如果PersonDao又依赖于另外一个对象A,那么就先尝试去实例化A……以此类推,是不是很像递归?

当所有依赖都被实例化出来之后,我们的

PersonService当然也被构造出来了。

问题又来了,如果PersonDao是一个接口呢?

Dagger怎么知道这个接口应该怎么实现?

答案是不知道的,那么Dagger怎么实例化出一个接口出来?

这个就是Module存在的意义之一了。

关于Module的讲解我

们会在后面详细说明,我们现在只要知道,Module里面会定义一些方法,这些方法会返回我们的依赖,就像:

@Module

publicclassPersonServiceModule{

/**

*提供PersonDao接口实例

*/

@Provides

PersonDaoprovidePersonDao(Aa){

returnnewPersonDaoImpl(a);

}

}

Dagger根据需求获取一个实例的时候,并不总是通过new出来的,它会优先查找Module

中是否有返回相应实例的方法,如果有,就调用Module的方法来获取实例。

比如你用@Inject注解了一个成员变量,Dagger会查找Module中是否有用@Provides注解的,返回该类实例的方法,有的话就会调用provide方法来获得实例,然后注入,如果没有的话Dagger就会尝试new出一个实例。

就像我们现在这个栗子,PersonService依赖于PersonDao接口,Dagger不能直接为我们new出一个接口,但我们可以提供一个Module,在Module中定义一个返回PersonDao接口实例的方法,这样,Dagger就可以解决实例化PersonDao的问题了。

我们再梳理一下流程,如果我们用@Inject注解了一个成员变量,并调用注入代码之后,Dagger会这样处理:

查找Module中是否有用@Provides注解的,返回该类实例的方法

如果有,就调用那个provide方法来获得实例,然后注入

如果没有,就尝试调用相应的类中被@Inject注解的构造方法new出一个实例,然后注入

如果没有一个构造方法被@Inject注解,Dagger会因不能满足依赖而出错

所以假如一个变量被@Inject注解,要么在Module中提供provide方法获取实例,要么该类提供一个被@Inject注解的构造方法,否则Dagger会出错

Module的使用

一般而言,Dagger会获取所有依赖的实例,比如当需要一个TestBean的时候,会通过newTestBean()创建实例并注入到类中。

但是,以下情况会就不好处理了:

需要生成的是一个接口,而Dagger不能直接实例化接口

不能在第三方库的类中添加注解

可配置的对象必须是配置的

为了解决以上问题,我们需要定义一个被@Module注解的类,在里面定义用@Provides注解的方法。

用该方法返回所需的实例。

@Module

publicclassPersonServiceModule{

@Provides

DprovideD(){

returnnewD();

}

@Provides

CprovideC(){

returnnewC();

}

@Provides

BprovideB(Cc,Dd){

returnnewB(c,d);

}

@Provides

AprovideA(Bb){

returnnewA(b);

}

/**

*提供PersonDao实例

*/

@Provides

PersonDaoprovidePersonDao(Aa){

returnnewPersonDaoImpl(a);

}

}

就像providePersonDao返回了PersonDao接口实例,Dagger虽然不能直接实例化出PersonDao接口,但却可以调用Module的providePersonDao方法来获得一个实例。

providePersonDao方法需要传入A的实例,那么这里也构成了一个依赖关系图。

Dagger会先获取A的实例,然后把实例传递给providePersonDao方法。

Component的使用

到目前为止,我们虽然知道了:

Dagger怎么获取实例:

从Module的provide方法中获取

通过@Inject注解的构造方法new出新的实例

Dagger会推导provide方法和构造方法的参数,形成依赖图,并“满足”我们依赖图的需求,获取依赖的实例

看样子需要注入的依赖可以获取了,但是不是总觉得还有点“零碎”,整个流程还没连贯起来?

比如,Module既然是一个类,生成依赖图的时候,怎么知道跟哪个Module挂钩?

即使最后生成了需要的实例,注入的“目的地”是哪里?

怎么才能把它注入到“目的地”?

残缺的这部分功能,正是Component提供的,Component起到了一个桥梁的作用,贯通Module和注入目标。

我们来看看最开始那个例子,我们是怎么进行依赖注入的:

publicclassMainActivityextendsAppCompatActivity{

@Inject

PersonServicemService;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

PersonServiceComponentcomponent=DaggerPersonServiceComponent.builder()

.personServiceModule(newPersonServiceModule())

.build();

//注入,所有@Inject注解的成员变量都会同时注入

component.inject(MainActivity.this);

//通过component获取实例,注意,这里只是演示用法,其实mService在component.inject的时候已经完成了注入

mService=component.getPersonService();

}

}

这个DaggerPersonServiceComponent是什么鬼?

DaggerPersonServiceComponent其实是Dagger为我们自动生成的类,它实现了一个Component接口(这个接口是需要我们自己写的),我们来看下它实现的接口长什么样子:

/**

*指定PersonServiceModule,当需要获取某实例的时候,会查找PersonServiceModule中是否有返回相应类型的方法,有的话就通过该方法获得实例

*

*@authorHansChen

*/

@Component(modules=PersonServiceModule.class)

publicinterfacePersonServiceComponent{

/**

*查找activity中被@Inject注解的成员变量,并尝试获取相应的实例,把实例赋给activity的成员变量

*注意函数格式:

返回值为空、带有一个参数

*/

voidinject(MainActivityactivity);

/**

*Dagger会尝试从Module中获取PersonService实例,如果Module中不能获取对应实例,则通过PersonService的构造方法new出一个实例

*注意函数格式:

参数为空,返回值非空

*/

PersonServicegetPersonService();

}

这个接口被Component注解修饰,它里面可以定义3种类型的方法:

返回值为空,有一个参数:

查找参数中被@Inject注解的成员变量,并尝试获取相应的实例(通过Module的provide方法或@Inject注解的构造方法new出新的实例),把实例赋给参数的成员变量

返回值非空,参数为空:

获取相应实例并返回

返回值是Component,参数是Moduld,通过该方法可以创建SubComponent实例

既然获取实例的时候,有可能用到Module,那么就必须为这个Component指定使用的Module是什么。

具体做法就是在@Component注解中指定modules。

定义好Component之后,Dagger会自动帮我们生成实现类,这就是Dagger强大的地方!

生成的类名格式是:

Dagger+Component名。

Component提供了2种方法,一个是注入式方法,一个是获取实例方法。

具体用什么方法,就看个人需求了。

一个Component其实也对应了一个依赖图,因为Component使用哪个Module是确定不变的,依赖关系无非也就是跟Module和类的定义有关。

一旦这些都确定下来了,在这个Component范围内,依赖关系也就被确定下来了。

额外再说一点,在Dagger1中,Component的功能是由ObjectGraph实现的,Component是用来代替它的。

Component定义好之后,build一下工程,Dagger就会自动为我们生成实现类了,就可以使用自动生成的实现类来进行依赖注入了。

到现在为止,我们已经通过Dagger完成了依赖注入。

可能看起来比正常方法麻烦得多,但是Dagger框架可以让依赖的注入和配置独立于组件之外,它帮助你专注在那些重要的功能类上。

通过声明依赖关系和指定规则构建整个应用程序。

熟悉完Dagger基本的使用之后,接下来我们来讲解一些稍微高级一点的用法:

Dagger的进阶使用

Components之间的关系

在Dagger中,Component之间可以有两种关系:

Subcomponents和Componentdependencies。

他们有什么作用呢?

比如在我们应用中,经常会有一些依赖我们在各个界面都使用得到,比如操作数据库、比如网络请求。

假设我们有个ServerApi的接口,在页面A、B、C都使用到了,那么我们要在页面A、B、C的Component里面都能获取到ServerApi的实例,但显然,获取ServerApi实例的方法都是一样的,我们不想写重复的代码。

于是我们可定义一个ApplicationComponent,在里面返回ServerApi实例,通过Component之间的关系便可以共享ApplicationComponent提供的依赖图。

下面通过Android中的一个小栗子来说明Subcomponents和Componentdependencies如何使用

dependencies

先说明下各个模块之间的关系

首先,我们定义一个ApplicationComponent,它定义了一个方法,通过它来获得ServerApi实例。

ApplicationComponent还关联了ApplicationModule,这个Module是ServerApi实例的提供者,注意,这个Moduld还可以返回Context实例

@Component(modules=ApplicationModule.class)

publicinterfaceApplicationComponent{

ServerApigetServerApi();

}

@Module

publicclassApplicationModule{

privatefinalContextmAppContext;

ApplicationModule(Contextcontext){

mAppContext=context.getApplicationContext();

}

@Provides

ContextprovideAppContext(){

returnmAppContext;

}

@Provides

ServerApiprovideServerApi(Contextcontext){

returnnewServerApiImpl(context);

}

}

publicclassDemoApplicationextendsApplication{

privateApplicationComponentmAppComponent;

@Override

publicvoidonCreate(){

super.onCreate();

mAppComponent=DaggerApplicationComponent.builder().applicationModule(newApplicationModule(this)).build();

}

publicApplicationComponentgetAppComponent(){

returnmAppComponent;

}

}

//注意,这里有个dependencies声明

@Component(pendencies=ApplicationComponent.class,modules=MainPresenterModule.class)

publicinterfaceMainPresenterComponent{

MainPresentergetMainPresenter();

}

@Module

publicclassMainPresenterModule{

privateMainViewmMainView;

publicMainPresenterModule(MainViewmainView){

this.mMainView=mainView;

}

@Provides

MainViewprovideMainView(){

returnmMainView;

}

}

publicclassMainPresenter{

privateMainViewmMainView;

privateServerApimServerApi;

@Inject

publicMainPresenter(MainViewmainView,ServerApiserverApi){

this.mMainView=mainView;

this.mServerApi=serverApi;

}

}

Component中getMainPresenter的目的很简单,就是返回MainPresenter,而MainPresenter又依赖MainView和Serve

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 历史学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1