一.作用域Scope

之前了解RoboGuice的时候,我们知道它默认给我们提供了几个注解,ContextSingleton和Singleton,但是Dagger2更为灵活,只有javax包中提供的Singleton注解。更为强大的是,我们可以自定义作用域。

首先我们定义一个运行时的注解。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity { }

我们新增加一个Module,专门为Activity中提供依赖。

@Module
public class ActivityUtilModule {
private Activity activity; public ActivityUtilModule(Activity activity) {
this.activity = activity;
} @Provides
public Resources provideResource() {
return activity.getResources();
} @Provides
@PerActivity
public Toaster provideToaster() {
return new Toaster(activity);
}
}

然后我们新增一个Component来承载这个Module,注意,这里我用了dependencies,这样Component就可以传递依赖到子Component。

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityUtilModule.class)
public interface ActivityComponent {
void inject(MainActivity activity);
}

我们修改ApplicationComponent,让其不再直接注入到Activity中,而使用ActivityComponent来注入。

在MainActivity中我们这样写:

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity"; @Inject
Toaster toaster; @Inject
@Named("app")
Context context; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent activityComponent = DaggerActivityComponent.
builder().
activityUtilModule(new ActivityUtilModule(this)).
applicationComponent(component()).
build();
activityComponent.inject(this); toaster.longToast(context.getPackageName()); } protected ApplicationComponent component() {
return ((DaggerApplication) getApplication()).Component();
}
}

运行成功后,会弹出包名。相信到这里,大家还是一头雾水。

当我们去掉ActivityComponent中的PerActivity注解时,我们就会发现,编译出错了:这里告诉我们没依赖这个注解,就使用了这个模块,这是编译时期就会不通过的。这里也暴露了作用域的作用,约束一个模块的注入器组件。且在这个组件中使用这个Module时,提供的这个Provider是单例。即组件模块内单例。

Error:(10, 1) 错误: github.pedroneer.dagger2.dagger.ActivityComponent (unscoped) may not reference scoped bindings:
@Provides @github.pedroneer.dagger2.dagger.PerActivity github.pedroneer.dagger2.Toaster github.pedroneer.dagger2.dagger.ActivityUtilModule.provideToaster()

好,也许到这里还是不懂,先看下这个例子。

我们将BaseActivity中注入了依赖。

public class BaseActivity extends FragmentActivity {

    @Inject
Toaster toaster; @Inject
@Named("app")
Context context; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent activityComponent = DaggerActivityComponent.
builder().
activityUtilModule(new ActivityUtilModule(this)).
applicationComponent(component()).
build();
activityComponent.inject(this); toaster.longToast(context.getPackageName());
} private ApplicationComponent component() {
return ((DaggerApplication) getApplication()).Component();
}
}

此外,我们MainActivity和SecondActivity都继承自BaseActivity,这也符合我们平时的做法。

public class MainActivity extends BaseActivity {
private static final String TAG = "toaster"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toaster.longToast(context.getPackageName());
Log.d(TAG, "onCreate: " + toaster.hashCode());
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
}
public class SecondActivity extends BaseActivity {
private static final String TAG = "toaster"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toaster.longToast(context.getPackageName());
Log.d(TAG, "onCreate: " + toaster.hashCode());
}
}

那接下来我们打印的两个hashCode会是同一个么?

当然不是。

03-21 00:32:57.706 12716-12716/github.pedroneer.dagger2 D/toaster: onCreate: 531688530
03-21 00:32:57.866 12716-12716/github.pedroneer.dagger2 D/toaster: onCreate: 204017154

因为我们在BaseActivity中就指定了每次创建时都会创建一个新的ActivityComponent,但ActivityComponent中依赖的ApplicationComponent确是一个,因为我们每次都是从DaggerApplication中拿这个对象注入的。

也就是说,注解了PerActivity的对象,只在一个模块的组件生命周期内单例。

再看下面的例子:

我们提供custom和the两种toaster。

@Module
public class ActivityUtilModule {
private Activity activity; public ActivityUtilModule(Activity activity) {
this.activity = activity;
} @Provides
public Resources provideResource() {
return activity.getResources();
} @Provides
@PerActivity
@Named("custom")
public Toaster provideToaster() {
return new Toaster(activity);
} @Provides
@Named("the")
public Toaster provideTheToaster() {
return new Toaster(activity);
}
}

并在ActivityComponent中提供依赖。

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityUtilModule.class)
public interface ActivityComponent {
void inject(BaseActivity activity); @Named("custom")
Toaster theToaster(); @Named("the")
Toaster toaster();
}

最后我们在MainActivity中查看下多次获取这个两个依赖的情况。

public class MainActivity extends BaseActivity {
private static final String TAG = "toaster"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: " + activityComponent.theToaster().hashCode());
Log.d(TAG, "onCreate: " + activityComponent.theToaster().hashCode());
Log.d(TAG, "onCreate: " + activityComponent.toaster().hashCode());
Log.d(TAG, "onCreate: " + activityComponent.toaster().hashCode());
}
}
03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 1028556617
03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 1028556617
03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 857606222
03-21 00:46:34.836 24014-24014/github.pedroneer.dagger2 D/toaster: onCreate: 614825327

答案很明显,我们定义了PerActivity中的custom修饰的Toaster每次都是唯一的,而the修饰的则每次创建一个对象。这就是作用域及无作用域的区别。

总结一下,当我们为一个组件打上标签作用域,那么这个组件的生命周期内,模块内的依赖也就随着组件的生命周期消亡而消亡,如果模块内存在提供作用域的Provider,而使用这个模块的组件不标注该作用域,则编译报错。Provider中提供的方法在同一模块内每次调用都会初始化,而标明了作用域的则不会。

二.绑定类型

这个上面的例子已经用过了,使用方法和RoboGuice相同,Dagger2也增加了Qualifier的概念,即可以自定义注解标示组件提供需要的注入类型。这里不做赘述。

三.懒加载

Dagger2支持Lazy Load,即在我们真正使用对象的时候才回去初始化。

public class MainActivity extends BaseActivity {

    @Inject
@Named("the")
Lazy<Toaster> toasterLazy; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (something()) {
toasterLazy.get().longToast("hello");
}
} private boolean something() {
return new Random().nextBoolean();
}
}

这样大大的提高了我们的效率,比如在BaseActivity中注入了多种依赖,如果不希望创建的时候即初始化,可以使用懒加载,只有在使用的时候初始化。

Dagger2 (二) 进阶篇的更多相关文章

  1. Sass进阶之路,之二(进阶篇)

    Sass之二(进阶篇) 1. 数据类型 1.1 Number 数字类型,小数类型,带有像素单位的数字类型,全部都属于Number类型 Number类型详情请点击这里,下面是小例子 1.$n1: 1.2 ...

  2. Sass之二(进阶篇)

    源码链接:http://pan.baidu.com/s/1o8M51hC 1. 数据类型 1.1 Number 数字类型,小数类型,带有像素单位的数字类型,全部都属于Number类型 Number类型 ...

  3. CocoaPods详解之(二)----进阶篇

    CocoaPods详解之----进阶篇 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/19178709 转载请注明出处 ...

  4. WPF 4 DataGrid 控件(进阶篇二)

    原文:WPF 4 DataGrid 控件(进阶篇二)      上一篇<WPF 4 DataGrid 控件(进阶篇一)>中我们通过DataGridTemplateColumn 类自定义编辑 ...

  5. idea 插件的使用 进阶篇

    CSDN 2016博客之星评选结果公布    [系列直播]零基础学习微信小程序!      "我的2016"主题征文活动   博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...

  6. 2. web前端开发分享-css,js进阶篇

    一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无从下手 ...

  7. windows系统快捷操作の进阶篇

    上次介绍了windows系统上一些自带的常用快捷键,有些确实很方便,也满足了我们的一部分需求.但是我们追求效率的步伐怎会止步于此?这一次我将会进一步介绍windows上提升效率的方法. 一:运行 打开 ...

  8. python 面向对象(进阶篇)

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  9. 最快让你上手ReactiveCocoa之进阶篇

    前言 由于时间的问题,暂且只更新这么多了,后续还会持续更新本文<最快让你上手ReactiveCocoa之进阶篇>,目前只是简短的介绍了些RAC核心的一些方法,后续还需要加上MVVM+Rea ...

随机推荐

  1. CacheManager:–个通用缓存接口抽象类库

    CacheManager是–个缓存通用接口抽象类库,它支持各种高速缓存提供者,例如Memcache,Redis,并且有许多先进的功能特性.具体可以访问官方网站  http://cachemanager ...

  2. ABP框架理论研究总结(典藏版)

    目前,我已经完成了Module-Zero的翻译,请查看我的<Module-Zero学习目录>. 到现在为止,使用ABP框架开发正式项目已经3个月有余了,期间翻阅了大量文档资料,包括ABP官 ...

  3. Linux 桌面系统字体配置要略

    字体显示效果测试 这一段是为了测试宋体字的显示效果,包括宋体里面自带的英文字体,“This is english,how does it look like?”.这一行是小字.后面几个字是加粗的宋体. ...

  4. 将mvc2升级到mvc4

    万万没想到大二时候参加中国大学生计算机大赛的一个项目正是当前项目所需! 时间紧任务重,还好之前的项目稍微改改即可交付使用,但是还是缺少一些功能模块,巧合的是,缺少的这个功能模块是今年刚刚完成的基于mv ...

  5. .NET里简易实现IoC

    .NET里简易实现IoC 前言 在前面的篇幅中对依赖倒置原则和IoC框架的使用只是做了个简单的介绍,并没有很详细的去演示,可能有的朋友还是区分不了依赖倒置.依赖注入.控制反转这几个名词,或许知道的也只 ...

  6. Go语言实战 - revel框架教程之CSRF(跨站请求伪造)保护

    CSRF是什么?请看这篇博文“浅谈CSRF攻击方式”,说的非常清楚. 现在做网站敢不防CSRF的我猜只有两种情况,一是没什么人访问,二是局域网应用.山坡网之前属于第一种情况,哈哈,所以至今没什么问题. ...

  7. [MongoDB] 32Bit构建上文件大小限制问题

    一. 问题概述 今天看看爬虫抓取的数据,发现数据无法插入,首先想到的就是32Bit构建的文件大小限制问题,检查一下还真的是.本文把整个检查问题,解决问题的过程记录下来. 问题:     "s ...

  8. ABP框架 - 缓存

    文档目录 本节内容: 简介 ICacheManager ICache ITypedCache 配置 实体缓存 EntityCache 是如何工作 Redis 缓存集成 简介 ABP提供了一个缓存接口, ...

  9. Windows 批处理

    1. 引言     在Windows上,经常需要做一些重复的工作.比如在不同的工作场所需要切换不同的ip:比如有时需要对一堆文件按1~n进行重命名:再比如我们需要删除一大堆文件,这些文件名字都差不多, ...

  10. Oracle位图索引

    索引由KEY和Data组成 位图索引的KEY比普通非唯一性索引多包含一个组成部分,分区,分区是将数据按行由内部机制分段以达到比较好的检索效率 位图索引的Data中,该索引KEY中数据值在分区段中按行分 ...