Dagger2 探索记3——两大进阶组件(一)
今天要讲的时@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。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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/second_text_1"
/> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_text_2"
/> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_text_3"
/> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_text_4"
/> </LinearLayout>
工具用过了,这次我们改喝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>
给全局的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的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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"
/> </LinearLayout>
看得出来我使用的还是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 探索记1——四大基本组件(一)
喝很多自主学习的人,我接触Dagger 2 框架的原因是刚进公司的时候导师给安排的学习任务,学习方式是组内培训. 听到这个消息的我,以为是部门的人轮流给我讲课. 后来导师跟我说,组内培训的意思是,我先 ...
- Dagger2 探索记2——四大基本组件(二)
书接上文,先回顾以下前一章写的内容. 内容大概就是在Activity中用@Inject标记一个注入的类,然后在这个类的构造函数上也打个@Inject标记,然后使用@Component来连接两边,完成对 ...
- 如何使用 K8s 两大利器"审计"和"事件"帮你摆脱运维困境?
概述 下面几个问题,相信广大 K8s 用户在日常集群运维中都曾经遇到过: 集群中的某个应用被删除了,谁干的? Apiserver 的负载突然变高,大量访问失败,集群中到底发生了什么? 集群节点 Not ...
- Java轻量级业务层框架Spring两大核心IOC和AOP原理
IoC(Inversion of Control): IOC的基本概念是:不创建对象,但是描述创建它们的方式.在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务.容器负责将这些 ...
- webpack 教程 那些事儿03-webpack两大精华插件,热加载
本节主要讲述 webpack的两大经典开发调试插件,热插拔内存缓存机制 文章目录 1. html-webpack-plugin插件的使用 2. webpack-dev-middleware 插件登场 ...
- 星环大数据安全组件Guardian与hadoop自带的安全组件区别
在进行讲解之前,先带大家学习下hadoop关于hdfs自己的安全如何实现的--------------------------- 名词: ACL-访问控制列表(Access Control List, ...
- 通过微信Android和iOS版,看两大系统的差异
由于设计师或者产品经理使用的移动设备大部分是iPhone,所以在做设计时,容易忽略Android和iOS的差异,按照自己的使用习惯进行设计,导致大部分设计师或产品经理做出的设计都是基于iOS规范或习惯 ...
- 大数据领域两大最主流集群管理工具Ambari和Cloudera Manger
不多说,直接上干货! 目前啊,都知道,大数据集群管理方式分为手工方式(Apache hadoop)和工具方式(Ambari + hdp 和Cloudera Manger + CDH). 手工部署呢, ...
- Go 语言的下一个大版本:Go 2.0 被安排上了(全面兼容1.X,改进错误处理和泛型这两大主题)
今年 8 月 Go 开发团队公布了 Go 2.0 的设计草案,包括错误处理和泛型这两大主题.现在备受瞩目的 Go 2.0 又有了新动向 —— 昨日 Go 开发团队在其官方博客表示,Go 2 已经被安排 ...
随机推荐
- linux time命令的输出中“real”“user”“sys”的真正含义
下面转载的文章详细地介绍了time出来显示的“real”“user”“sys”的真正含义. Linux中time命令,我们经常用来计算某个程序的运行耗时,用户态cpu耗时,系统态cpu耗时. 例如: ...
- JAVA中位数排序
package quickSort; public class QuickSort { private static int count; /** * 测试 * @param args */ publ ...
- ubuntu 安装phpunit
一.下载安装 wget https://phar.phpunit.de/phpunit-7.2.phar chmod +x phpunit-7.2.phar sudo mv phpunit-7.2.p ...
- RandomAccessFile类学习
RandomAccessFile类学习 RandomAccessFile是io包的类,从Object直接继承而来,只可以对文件进行操作,可以对文件进行读取和写入. 当模式为r:当文件不存在时会报异常: ...
- appium---学习
一直想学但是没有时间,今天看到个不错的链接保存一下. 学习链接:http://www.testclass.net/appium_base/appium-base-summary
- FMX Android ZIP解压中文乱码
在手机上解压了一个WINDOWS上的压缩文件, 发现中文是乱码的,解决方法如下: 找到System.zip.pas文件 将E := TEncoding.GetEncoding(437); 改为 E ...
- springMVC接收请求参数的几种方式
1. 用注解@RequestParam绑定请求参数 用注解@RequestParam绑定请求参数a到变量a,当请求参数a不存在时会有异常发生,可以通过设置属性required=false解决,例如: ...
- Apache Flink 的迁移之路,2 年处理效果提升 5 倍
一.背景与痛点 在 2017 年上半年以前,TalkingData 的 App Analytics 和 Game Analytics 两个产品,流式框架使用的是自研的 td-etl-framework ...
- SSM项目用ajax来显示数据
<script type="text/javascript"> //1:页面加载完成后,直接去发送ajax请求,要到分页的数据 $(function(){ $.ajax ...
- php7和MongoDB插入并读取数据
php7和MongoDB插入并读取数据 代码如下: <?php $manager = new MongoDB\Driver\Manager("mongodb://localhost:2 ...