Dagger2 使用正确姿势。

Dagger2 使用正确姿势。

上一篇文章《Dagger2 这次入门就不用放弃了》中介绍了Dagger2的一些显浅的使用方式,我觉得是非常适合入门Dagger2的傻瓜式讲解,后来发现有些内容讲的不够仔细,有些细节没有详细解释清楚。参考了以下三篇文章后,对之前的内容进行一些补充。

以上这三篇文章对于Dagger2的思路理得很清晰,并且对于Dagger2做出了一个抽象的概念讲解,本文的一些内容也是摘自以上文章。

没有看过《Dagger2 这次入门就不用放弃了》可能不利于阅读

本文的内容:

本篇的内容同样不涉及到Dagger2的内部原理,只是对上一篇文章做了一些知识的补充,从而能够更好的使用Dagger2

  • 理清思路,Dagger2的两种提供注入实例的方式
  • 为什么ActivityComponent要提供一个inject方法。
  • Scope的使用,如何实现单例?
  • Qualifier限定符的作用以及使用。
理清思路,Dagger2的两种提供注入实例的方式。

之前说的是提供注入实例的方式是通过编写Moudle,并且提供一些provideXXX()的方法,然后通过Component把这些对象进行注入。

其实在Dagger2中还有一种方式实现对象的注入,这种方式比较简单。假设我们需要注入一个A类的对象,我们只需要在A类的构造函数中添加@Inject注解即可,然后在Component中直接提供一个方法获取A的实例就行了。代码如下:


public class A{

    @Inject
    public A(){
    }
}

//在  XXXComponent 中

A getA();

然后在我们需要注入A对象的地方(Activity),使用Component调用getA()方法即可获得这个A对象。

说到这里,可能也会有些疑问,这里的A类是一个简单的对象,假如A需要在构造函数传入参数怎么办?其实原理和上一篇文章说的差不多,这些参数会根据依赖的Component或者当前Componentmodule中提供的provideXXX()方法来获取。

这里的Dagger2总共就有这两种方式提供依赖的注入。事实上,经过测试,Dagger2在遇到@Inject修饰的成员变量的时候,会先查找Component中是否有提供对象,找不到的情况下,才会去找包含有@Inject的构造函数。

整个Dagger2的依赖注入的过程如下:

步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
    步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数
    步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,
           看构造函数是否存在参数
    步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数
    步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
为什么ActivityComponent要提供一个inject方法

首先经过我的测试,当我在DaggerActivity中对对某些需要注入的成员添加@Inject注解的时候,Dagger2就会生成一个DaggerActivity_MembersInjector的东西,这个是对成员变量进行注入的关键类,涉及到一些比较深入的内容,我们这里先不要深入探讨这里的运行机制。我们只需要知道当我们需要注入一些成员变量的时候,我们需要在ActivityComponent中提供一个方法:

void inject(DaggerActivity daggerActivity);

并且在初始化的时候调用这个方法,才能成功注入。至于这个方法inject是官方推荐的名称,我们可以不用这个,但是使用inject的话比较好理解吧。值得注意的一点是,这个方法的参数一定要准确,比如DaggerActivity需要注入,我们这里就需要些DaggerActivity,不能写成BaseActvity或者Activity,否则会注入失败。因为内部的原理是要对这个参数进行注入的,很显然在DaggerActivity中需要注入的内容在BaseActivity或者Activity中并不存在。

Scope的使用,如何实现单例?

这个迷之Scope也是有点难以理解,我们在哪里使用到了Scope呢。在我们的AppComponent中添加了一个注解为@Singleton@Singleton就是一个Scope,据说可以实现单例哟。。。难道这样就实现了单例模式?我刚刚开始是这样理解的。直到仔细的看了几遍这篇文章我才知道并不是这样的。

事实上@Sinleton中并没有创建单例的能力,那么AppComponent中提供的依赖注入是如何实现单例的呢。其实这个原理很贱单。

首先Module提供了创建实例的方法,接着AppComponent中对Module进行管理,最后AppComponent在自定义Applicaiton中被实例化了一次

这个实例化了一次是最重要的呀。仅仅被实例化了一次,那不就是单例么。就是这么简单呀。

可能有些童靴当时就不乐意了,那既然这样都已经实现了单例,那么这个@Singltop还要来何用?不是多此一举吗。

其实@Singletop还有有一些作用的,首先一方面能让你直面的了解到这是一个单例,其次这个@Singletop能够更好的管理ModlueComponent之间的关系。

Dagger2需要保证ComponentModule是匹配的,就需要用到这个注解。

为什么这样说,看过上一篇文章的人就会知道,我定义了一个ActivityScope,我之前的解释是说ActivityScope是为了对应Activity的生命周期(当时就很不理解,难道这个Scope真的这么有魔性,声明一个ActivityScope就能让提供的注入对象和Activity生命周期一致),很显然,这是错误的。为什么需要这个Scope呢?原因是因为我在AppComponent中是有@Singletop的,ActivityComponent中依赖了AppComponent,所以我们需要使用一个Scope来匹配他们之间的关系,不然就会在编译期间报错。并不是说ActivityScope能让实例和Activity生命周期一致。和Activity生命周期一致是因为ActivityComponent是在Activity中生成实例的。(具体Dagger2内部是如何保障这个的,并没有深入研究过)。

Qualifier 限定符的作用以及使用。

这也是一个很强大的注解,首先为什么需要用这么一个东西呢,之前说道过,在Module中的provide方法实际上是根据返回值来进行识别的。但是假设我需要根据不同的需求传入不同的构造参数的时候,如何区分呢?比如:一个Presenter,可能他有两个构造函数,分别对应不同的需求,这种情况下,provide方法的返回值都是Presenter,那么就需要使用Qualifier。具体怎么使用呢。

首先声明一个注解用@Qualifier修饰,然后在需要区别的地方添加就行了,下面是一个实例代码:

@Module
public class AppModule {
    private final App app;

    public AppModule(App app) {
        this.app = app;
    }

    @Provides
    @Singleton
    @ForApplication
    Context provideAppContext() {
        return app;
    }

    @Provides
    @Singleton
    Prefser providePrefser(@ForApplication Context context) {
        return new Prefser(context);
    }

    @Provides
    @Singleton
    AccountManager provideAccountManager(@ForApplication Context context) {
        return AccountManager.get(context);
    }

首先在provide中添加一个注解@ForApplicition,接着在需要使用这个Context的地方再次标示即可。这样假设有其他一些提供了ActivityContext的地方和这里发生冲突的时候,Dagger2也能准确找到这个ApplicaitonContext

总结

这里对Dagger的总体使用方式进行了一些补充,还有没有从源码的角度来理解Dagger2,下篇文章会结合Dagger2生成的代码进行讨论,只有认识了这个运行机制,我们才能运用得得心应手。

时间: 2024-08-11 03:35:19

Dagger2 使用正确姿势。的相关文章

程序员取悦女朋友的正确姿势---Tips(iOS美容篇)

前言 女孩子都喜欢用美图工具进行图片美容,近来无事时,特意为某人写了个自定义图片滤镜生成器,安装到手机即可完成自定义滤镜渲染照片.app独一无二,虽简亦繁. JH定律:魔镜:最漂亮的女人是你老婆魔镜:程序员不是木头人 核心技术 图片滤镜核心技术的基本思路如下: 核心技术流程 具体流程 1.创建一个图像处理工具类 注:该类实例包括一个图像处理方法,该方法在传入原始图像和一个颜色矩阵后生成一个处理好的图像. @interface JHFeilterManager : NSObject @proper

docker centos rpm离线安装1.8.2及pull的正确姿势

1.离线安装 本次只针对1.8.2版本,所需要的rpm包如下 docker-engine-1.8.2-1.el7.centos.x86_64.rpm(已上传51cto) libcgroup-0.41-8.el7.x86_64.rpm libcgroup-devel-0.41-8.el7.x86_64.rpm libcgroup-pam-0.41-8.el7.x86_64.rpm libcgroup-tools-0.41-8.el7.x86_64.rpm(libcgroup请参考开源镜像库) 简单

观看学习视频的正确姿势与姿态

开学至今,时时被二柱子逼得走投无路. 痛定思过,目前的自己确实是"三拍",一拍觉得这样那样一定可以胸有成竹,二拍有了点子拿起手术刀就开始实践,三拍无法实现代码拍屁股放弃.啊,我为了二柱子建了好多好多包,想过好多好多条"去北京的路",结果...还是死于基础太烂.也不愿意头悬梁锥刺股.所以呢,那就改进学习方式呗,提高效率. 整个开发过程中,自己犯了一个很大的错误.即是在第五周中途才突然发现,其实代码在整个学习过程中虽说是基础但不是没有它就建不成大楼.而我,白白的把4周的

Node.js中使用redis数据库的正确姿势

Redis是一个常用的Nosql数据库,一般用来代替Memcached做缓存服务,同时它也支持数据的持久化,有着比较广泛的应用场景.在Java中使用redis我们已经比较熟悉了,那么在node.js和koa.js框架中使用Redis的正确姿势是怎样的呢? Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: * Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载

重磅消息!河南省考正确姿势:安心过双节!(推迟后18届毕业生有福!)

重磅消息!河南省考正确姿势:安心过双节!(推迟后18届毕业生有福!) 小可爱们,说想我了没? 肯定想了是吧哈哈 自恋一分钟! 河南省考推迟消息已确定 正确姿势:安心过双节! 各位小伙伴可以愉快的过双节啦! 开心吧! 不信请看:这次小编不骗你啦! 证据

在Linux(ubuntu server)上面安装NodeJS的正确姿势

上一篇文章,我介绍了 在Windows中安装NodeJS的正确姿势,这一篇,我们继续来看一下在Linux上面安装和配置NodeJS. 为了保持一致,这里也列举三个方法 第一个方法:通过官网下载安装 https://nodejs.org/en/download/ 这种方式的问题是我们需要自己去找网页,找到链接,然后下载 第二个方法:使用apt工具进行安装 默认情况下,在apt的源中只有比较老的版本(注意,需要先apt-get update) 例如,如果运行apt-get install nodej

ios监听ScrollView/TableView滚动的正确姿势

主要介绍 监测tableView垂直滚动的舒畅姿势 监测scrollView/collectionView横向滚动的正确姿势 1.监测tableView垂直滚动的舒畅姿势 通常我们用KVO或者在scrollViewDidScroll代理方法中监听ScrollView/TableView的contentOffset,比如监听TableView的contentOffset来设置导航栏的透明度或者拉伸顶部的图片. image image 常见的姿势是在scrollViewDidScroll的代理方法中

使用layoutinflater的正确姿势

使用layoutinflater的正确姿势 一开始接触安卓开发的时候,知道layoutinflater是用来将布局文件生成对应的View.那时候还是懵懵懂懂知道需要传递一个layoutId一个parent参数和一个false参数.那时候就这样用,初初还是好好的.直到后来随着进一步学习安卓开发发现layoutinflater的这两个参数是有大大的门道在里面. 然后这一篇博客可以说是我对layoutinflater使用的一个总结. 怎么添加一个View到ViewGroup? 在讨论怎么使用layou

判断是否为gif/png图片的正确姿势

判断是否为gif/png图片的正确姿势 1.在能取到图片后缀的前提下 1 2 3 4 5 6 7 8 9 //假设这是一个网络获取的URL NSString *path = @"http://pic3.nipic.com/20090709/2893198_075124038_2.gif"; // 判断是否为gif NSString *extensionName = path.pathExtension; if ([extensionName.lowercaseString isEqua