dagger2 使用教程第二节

第二节引言
在上第一节中我们了解了如何引入Dagger,什么是依赖注入,对Dagger有了一个初步的认识。

总结@Inject, @Compoent
上一节我们使用了这两个字段,并进行了说明,现在我们先来对@Inject进行一下总结

  • 使用@Inject注释构造函数。当请求一个新实例时,Dagger将获得所需的参数值并调用这个构造函数
class User @Inject constructor()
{
    var name:String?=null
    override fun toString(): String {
        return "Name:$name"
    }
}
  • Dagger可以直接注入字段。在本例中,Dagger为user字段获得一个User实例。
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user:User
    ...
   }

以上两条要理解透彻,还有几条知道即可吧,因为我没太想到什么时候能有用。。。

  • 如果您的类有@Inject注解的字段,但没有@Inject注解的构造函数,那么Dagger会在被请求时注入这些字段,但不会创建新的实例。添加一个带@Inject注释的无参数构造函数,Dagger才会创建实例。
  • Dagger也支持方法注入,不过构造函数或字段注入通常是首选。
  • 缺少@Inject注解的类不能由Dagger构造。

这其实是官方文档的总结,但如果不结合实例很难理解它们

你可能已经注意到了,我们的User类没有参数,这个可以先不管。后边会进行说明。
接下来我们来具体看看@Component吧,相信通过上一节的介绍,对它算是有个印象了,那它到底是个什么呢?
@Component相当于是Dagger的调度中心,我们先来看看上一节用@Component注解的接口

@Component
interface MainComponent{
    fun inject(activity: MainActivity)
}

这段代码会让Dagger自动生成一个类,我还是贴一下吧

public final class DaggerMainComponent implements MainComponent {
  private DaggerMainComponent() {

  }

  public static Builder builder() {
    return new Builder();
  }

  public static MainComponent create() {
    return new Builder().build();
  }

  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectUser(instance, new User());
    return instance;
  }

  public static final class Builder {
    private Builder() {
    }

    public MainComponent build() {
      return new DaggerMainComponent();
    }
  }
}

本来不想分析源码,但发现不分析,光说结果不太清晰。。。
那么在来结合添加@Inject后生成的类

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<User> userProvider;

  public MainActivity_MembersInjector(Provider<User> userProvider) {
    this.userProvider = userProvider;
  }

  public static MembersInjector<MainActivity> create(Provider<User> userProvider) {
    return new MainActivity_MembersInjector(userProvider);
  }

  @Override
  public void injectMembers(MainActivity instance) {
    injectUser(instance, userProvider.get());
  }

  @InjectedFieldSignature("com.study.daggerbasic.MainActivity.user")
  public static void injectUser(MainActivity instance, User user) {
    instance.user = user;
  }
}

我们在MainActivity_MembersInjector看到两个方法

public void injectMembers(MainActivity instance)
@InjectedFieldSignature("com.study.daggerbasic.MainActivity.user")
  public static void injectUser(MainActivity instance, User user) {
    instance.user = user;
  }


记住@Component是用来注释接口的,从而生成一个Dagger开头的生成类,本例生成是:DaggerMainComponent。
被注释的接口要满足一个条件

  • @Component注释的接口或抽象类,必须至少包含一个抽象组件方法。组件方法可以有任何名称,但是必须具有符合members-injection注入契约的签名

什么是members-injection注入契约的签名? 没错,就是它!!!

public void injectMembers(MainActivity instance)

现在就解释了 我们为什么要在我们的接口中写这样的接口了

@Component
interface MainComponent{
    fun inject(activity: MainActivity)//符合members-injection注入契约的签名
}

你现在应该能举一反三,理解为何调用这个inject接口就能给user变量赋值了吧。(调试下,看看各个接口调用顺序就明白了)
(请注意这是一个连贯的教程,您必须从头看,并在机器上有真正的工程才能很好的理解。如果用的是java,翻译下代码就好了,代码很简单)
现在你可以把inject接口改个名字试试,来验证总结的正确性(当然需要clean,rebuild)

@Component
interface MainComponent{
    fun inject_hero(activity: MainActivity)
}

一切okay,看来总结的没问题(毕竟我这个总结就是@Component注释内容的翻译 :D)。
好吧,别高兴的太早,我们之前提出的问题还没解决呢,毕竟大多数时候类都是有参数的,哪有那么多无参构造类让您老@Inject呢? 既然这么麻烦,我不干程序员了,爱咋地咋地。本来想这么说,但想想自己除了敲代码好像啥都不会了,那还是继续吧。。。

先来看看官方自己对@Inject的吐槽吧:
Inject并不是在任何地方都有效:

  • 接口不能被构造。
  • 第三方类不能被注解(annotated)。
  • 可配置对象必须被配置!(Configurable objects must be configured!)

前两条好理解,我们来重点说说第三条:
现在我们修改User类,添加一个age字段到构造函数

class User @Inject constructor(val age:Int)
{
    lateinit var name:String
    override fun toString(): String {
        return "Name:$name"
    }
}

此时我们编译工程,发现编译不过。这也是显然的事情,没任何人配置了age字段。编译过才奇怪。
这就是所谓的“可配置对象必须被配置”,我们一会在来说如何改正这个错误。我们先来看看如果构造函数的参数也是被注入过的,又会如何呢,我们把User类改回最初的模样,然后在添加一个新的测试类叫做Dog,界面上在添加一个按钮显示Dog的名字以及其主人的名字


dog.png

先来写Dog类,并修改@Componet修饰的接口MainComponent。通过之前的学习,你应该能知道Dog的owner字段是如何被创建的!!!

class Dog @Inject constructor(var owner:User)
{
    lateinit var name: String
    override fun toString(): String {
        return "Dog:$name, Owner:${owner.name}"
    }
}
@Component
interface MainComponent{
    fun inject(activity: MainActivity)
}

在来贴一下MainActivity的代码,还是很简单

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user:User
    @Inject lateinit var dog:Dog
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerMainComponent.builder().build().inject(this)
        user.name="Hero"
        dog.name="大黄"
        dog.owner.name="小明"
        buttonUser.setOnClickListener {
            textViewInfo.text = user.toString()
        }
        buttonDog.setOnClickListener {
            textViewInfo.text = dog.toString()
        }
    }
}

此时运行,一切okay。此时你观察生成类的注入签名方法,会发现Dagger2已经发现Activity的dog里面有个owner他会自动帮我们创建实例

  @Override
  public void injectMembers(MainActivity instance) {
    injectUser(instance, userProvider.get());
    injectDog(instance, dogProvider.get());
  }
  @InjectedFieldSignature("com.study.daggerbasic.Dog.owner")
  public static void injectOwner(Dog instance, User owner) {
    instance.owner = owner;
  }

这进一步阐述了"可配置对象必须被配置", 你在dog内配置了owner因为owner本身是可注入的,一切都是通过注入完成的!如果你看完这个教程,在去看官方文档,会轻松很多,不然看到这句话会很蒙圈的。
解释一句话还真是,可能我有强迫症,生怕我说的不清楚吧。当然要特别特别注意,这都是我的理解,如果有问题,请您指正,我会非常感谢您的。毕竟我刚看到这句话时,很蒙圈。
现在回到最初的问题, 如果User有参数,而且参数还是不可注入的,那咋办?dagger2说,这我考虑到了,但你这篇文章篇幅过长了吧,不如下节再说吧。
我说:好的,我们下节见!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353