热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dagger2探索记3——两大进阶组件(一)

今天要讲的时@Scope这个组件。为什么说它是进阶组件,就是因为它基本上没作用,但在理解了基本组件之后又必须用到。Scope的意思是作用域,一般用来标记@Provide方法,将生成

       今天要讲的时@Scope这个组件。为什么说它是进阶组件,就是因为它基本上没作用,但在理解了基本组件之后又必须用到。

       Scope的意思是作用域,一般用来标记@Provide方法,将生成的对象单例化。但@Scope不能直接使用,需要先实现。默认实现有@Singleton。

一 局部单例

       话说千百句,不如码二行。

       直接上代码,就不在原来的代码上改了,另起一个Activity,命名为SecondActivity。

public class SecondActivity extends AppCompatActivity {
@BindView(R.id.second_text_1) TextView text1;
@BindView(R.id.second_text_2) TextView text2;
@BindView(R.id.second_text_3) TextView text3;
@BindView(R.id.second_text_4) TextView text4;

@Inject Coffee coffee1;
@Inject Coffee coffee2;
@Inject Tools tool1;
@Inject Tools tool2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
ButterKnife.bind(this);

SecondComponent compOnent= DaggerSecondComponent.builder()
.secondModule(new SecondModule())//添加Module
.build();

coffee1 = component.coffee();
coffee2 = component.coffee();
tool1 = component.tools();
tool2 = component.tools();

text1.setText(coffee1.toString());
text2.setText(coffee2.toString());
text3.setText(tool1.toString());
text4.setText(tool2.toString());
}
}

       然后是Layout命名为second。


android:layout_
android:layout_
android:orientation="vertical">

android:layout_
android:layout_
android:id="@+id/second_text_1"
/>

android:layout_
android:layout_
android:id="@+id/second_text_2"
/>

android:layout_
android:layout_
android:id="@+id/second_text_3"
/>

android:layout_
android:layout_
android:id="@+id/second_text_4"
/>

        工具用过了,这次我们改喝Coffee。

public class Coffee {
    public Coffee(){};
}

        其实没啥变化,主要是不和之前的代码混淆,影响理解。

        时刻记住,一个阶段结束了,就要换个起点重新开始。

        下面是Module和Component:

@Module
public class SecondModule {

@Singleton
@Provides
Coffee provideCoffee(){
return new Coffee();
}

@Provides
Tools provideTools(){
return new Tools();
}
}
@Singleton
@Component(modules = SecondModule.class)
public interface SecondComponent {
Coffee coffee();

Tools tools();
}

          细心的小伙伴看完这些代码就会发现,这次的Component里面,没有写inject()方法!

          没错,记得我前面说过,在使用inject()方法之前,返回的是一个Component。

          还标了是考点,不记得的小伙伴重修一遍第一章。

          所以这次是手动调用Component中的方法来进行赋值。

          最后在MainActivity中加个跳转Button,layout我就不贴了,代码看多了容易脱发。

    @OnClick({R.id.turn_firstactivity,R.id.turn_secondactivity})
    public void onViewClicked(View view) {
        switch (view.getId()){
            case R.id.turn_firstactivity:
                startActivity(new Intent(this, FirstActivity.class));
                break;
            case R.id.turn_secondactivity:
                startActivity(new Intent(this, SecondActivity.class));
                break;
        }
    }

          是不是突然发现了ButterKnife的好处,简化了很大一部分代码,直接依靠id设置点击事件,不用管是不是Button。

          来我们好好看看DaggerComponen中变化的源码。

  private void initialize(final Builder builder) {

    this.provideCoffeeProvider =
        DoubleCheck.provider(SecondModule_ProvideCoffeeFactory.create(builder.secondModule));

    this.provideToolsProvider = SecondModule_ProvideToolsFactory.create(builder.secondModule);
  }

  @Override
  public Coffee coffee() {
    return provideCoffeeProvider.get();
  }

  @Override
  public Tools tools() {
    return provideToolsProvider.get();
  }

        重写了Component中的两个方法,并各自对应上了相应的工厂类的实例,直接return get()方法。

        运行一下,发现结果是

技术图片

        两个用@Singleton标记了@Provide的Coffee实例的哈希地址一致,因为是同一个对象。(有兴趣的小朋友可以更改一个类的成员变量,看看另一个会不会变。)

        @Singleton就是这个用处,叫做局部单例。注意,不是全局单例,只在同一个Activity中是相同的,跨Activity就不同了。

        注意用@Singleton标记的@Provide的@Module绑定@Component时,@Component也要标记@Singleton,表示@Component中有@Singleton单例方法。

        这句话很绕,多看几遍就懂了。

        至于单例是怎么实现的,我们看看源码。

    this.provideCoffeeProvider =
        DoubleCheck.provider(SecondModule_ProvideCoffeeFactory.create(builder.secondModule));

    this.provideToolsProvider = SecondModule_ProvideToolsFactory.create(builder.secondModule);

       问题就出在这个DoubleCheck上面,有兴趣的朋友可以去看看DoubleCheck的实现源码,我太懒了,懒得讲。

       那么,既然@Singleton是局部单例,怎么实现全局单例呢?接下来就来讲。

二 全局单例

       前面说过,@Scope是作用区域,我们使用的是在Activity中实例化,所以是在Activity中局部单例。那么全局单例就得在Application中实例化。

       我们先写个自己的Application来注入依赖。

public class MyApplication extends Application {

    private static GlobalComponent component;

    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerGlobalComponent.builder()
                .globalModule(new GlobalModule())
                .build();
    }

    GlobalComponent get(){
        return component;
    }
}

         因为重写了Application,需要在Manifiest中把它添加进去

    <application
        android:name="MyApplication"
        ......
    

        给全局的Application注入,前面写的很清楚了,后面直接贴代码。

        再来个类Global,全球化一点。

public class Global {
    public Global(){};
}

        Component:

@Component(modules = GlobalModule.class)
public interface GlobalComponent {

    Global global();

}

         Module:

@Module
public class GlobalModule {

    @Provides
    Global provideGlobal(){
        return new Global();
    }

}

       如果前面好好听讲了的同学,现在应该已经能看懂了。我们在MyApplication中注入了一个Component的实例,并写了个get()方法来获取它。

       后续需要就直接获取MyApplication中的Component实例即可。

       例如前面的SecondActivity怎么注入Global实例呢?仅仅需要在SecondComponent中依赖GlobalComponent即可,可以理解为继承。

       看代码!

@Singleton
@Component(modules = SecondModule.class,dependencies = GlobalComponent.class)
public interface SecondComponent {
    void inject(SecondActivity activity);
}

      在括号中用dependence即可,这里我要特别说一点. 我说完了。

      可能又有细心的盲僧发现了华点。没错,怎么又改回用inject了?难道dependence就必须用inject方法吗?

      先表扬一下这位同学,观察很细致。

      答案是:不是。只是我懒得在后面写代码了。

      改回来多方便,前面是为了给你们演示才特意写了一下。像我这么懒的人自然是怎么简单怎么来。

      SecondActivity也改了,记得要编译,不然会标红。

public class SecondActivity extends AppCompatActivity {
    @BindView(R.id.second_text_1)    TextView text1;
    @BindView(R.id.second_text_2)    TextView text2;
    @BindView(R.id.second_text_3)    TextView text3;
    @BindView(R.id.second_text_4)    TextView text4;

    @Inject    Coffee coffee1;
    @Inject    Coffee coffee2;
    @Inject    Global global1;
    @Inject    Global global2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second);
        ButterKnife.bind(this);

        DaggerSecondComponent.builder()
                .globalComponent(new MyApplication().get())
                .secondModule(new SecondModule())//添加Module
                .build()
                .inject(this);

        text1.setText(coffee1.toString());
        text2.setText(coffee2.toString());
        text3.setText(global1.toString());
        text4.setText(global2.toString());
    }
}

         把之前的参照组改成了实验组,实验组改成了参照组。(上过初中生物的理科生应该懂。)

         因为绑定了SecondMoudle所以调用secondMoudle,这次继承了GlobalComponent,自然也要调用,输入参数是Component。

         跑一下就能发现,global注入进去了。

技术图片

         但是,很明显不是单例。因为我们还没有给它变形。

         好了,敲黑板,别低头看书了,都看我,我要开始变形了。

         先讲一下要点。@Scope不能直接用,我们之前用的是默认实现的一个@Singleton,先看看它的源码。

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

          我们自己也来设置一个:

@Scope
@Documented
@Retention(RUNTIME)
public @interface MyScope {
}

         很简单对不对?照着抄就好了,就改了个名字。

         要是爱情也这么简单就好了。

         现在是动手时间,把SecondModule和SecondComponent中的@Singleton替换成@MyScope,然后跑一遍,看看结果是不是一样的。

         我去上个厕所......

         跑好的同学,去给GlobalModule和GlobalComponent中加上@Singleton。再跑一遍。

         一般上课都是这样,前三十分钟老师讲课,后十五分钟学生做练习。

         我写些代码,你们也要练一下。

         让我猜猜,有些调皮的同学,趁着老师上厕所没回来的时间,偷偷看了后面的练习题,想要偷个懒。

         于是很省力的,直接把@MyScope加在了GlobalModule和GlobalComponent里面。

         然后就报错了。

错误: This @Singleton component cannot depend on scoped components:
@com.yuanxixing.dagger2.MyScope com.yuanxixing.dagger2.GlobalComponent

         必须先表扬这一批同学,选择了少有人走的路。

         哪怕是错的,也只是发现了一条不可行的方法!

         确实,这是一个隐藏的成就。

         先讲讲Component的继承,也就是Dependence。

         一个被@Scope标记的Component,在被继承时,继承它的Component也必须被@Scope标记。这里的原因应该是,继承Component时,就继承了它提供的方法,但是我们之前说过,给Component标记@Scope就是意味着里面有单例的@Provide,所以继承了单例@Provde的Component也要打上@Scope。

         并且这两个@Scope还不能相同。我前面要求做法是用@MyScope标记的SecondComponent来dependence被@Singleton标记的GlobalComponent。

         但是有的同学反过来试了,用@Singleton标记的SecondComponent来dependence被@MyScope标记的GlobalComponent于是就报错了。

         我也尝试了几种情况,用两个自定义的@Scope标记Component继承,没问题。

         但是@Singleton就是不一样,它标记的Component就是只能被dependence,它就是高人一等,它标记的Component不需要也不能依靠别人。

         所以,记住就行。

         好了,最后老师再辛苦一下,写个ThridActivity测试一下Global是不是全局单例。

public class ThridActivity extends AppCompatActivity {
    @BindView(R.id.thrid_text_1)    TextView text1;
    @BindView(R.id.thrid_text_2)    TextView text2;
    @BindView(R.id.thrid_text_3)    TextView text3;
    @BindView(R.id.thrid_text_4)    TextView text4;

    @Inject    Coffee coffee1;
    @Inject    Coffee coffee2;
    @Inject    Global global1;
    @Inject    Global global2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.thrid);
        ButterKnife.bind(this);

        DaggerSecondComponent.builder()
                .globalComponent(new MyApplication().get())
                .secondModule(new SecondModule())//添加Module
                .build()
                .inject(this);

        text1.setText(coffee1.toString());
        text2.setText(coffee2.toString());
        text3.setText(global1.toString());
        text4.setText(global2.toString());
    }
}

           layout的代码:



    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/thrid_text_1"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/thrid_text_2"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/thrid_text_3"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/thrid_text_4"
        />

           看得出来我使用的还是DaggerSecondComponent来注入,所以我需要再SecondComponent中加上代码,适配一下ThrdActivity。

@MyScope
@Component(modules = SecondModule.class,dependencies = GlobalComponent.class)
public interface SecondComponent {
    void inject(SecondActivity activity);
    void inject(ThridActivity activity);
}

          然后在SecondActivity中加入一个点击跳转,button自己去加。

    @OnClick({R.id.turn_thridactivity})
    public void onViewClicked(View view) {
        startActivity(new Intent(this, ThridActivity.class));
    }

           然后跑完就可以看。。。。。。

           妈的!虚拟机崩了。

          算了,你们自己跑吧!

          下一章再讲源代码。

Dagger2 探索记3——两大进阶组件(一)


推荐阅读
  • 开发笔记:Dagger2 探索记3——两大进阶组件
        今天要讲的时@Scope这个组件。为什么说它是进阶组件,就是因为它基本上没作用,但在理解了基本组件之后又必须用到。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • 1.在gradle中添加依赖在主项目的build.gradle中添加Dagger2库的依赖dependencies{compilecom.google.dagger:dagger: ... [详细]
  • android 自定义模板下载,android studio 自定义模板
    由于项目用上了mvp架构,基本上一个页面就至少需要新创建6个类,分别是modelviewpresenter的接口以及其对应的实现类,再加上使用dagger的话就要更多了,所以这时候 ... [详细]
  • Uberlicenseforandroidlist:1.ButterKnife:项目地址:https:github.comJakeWhartonbutterknife这个开源库可以 ... [详细]
author-avatar
Federer2012_554
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有