之前写过一篇文章介绍Dagger2的初步知识, 本篇文章主要介绍Dagger2的进阶知识点.

主要包含的内有有

  • @Binds与@Provides的使用
  • Provider与Lazy的使用
  • 依赖与包含
  • Dagger.Android

@Binds与@Provides

相信大家经常会使用@Provides来在Module里面提供需要注入对象的构造, 但从来没有用过@Binds.

如果我们需要注入一个接口的实现,我们常常会这么做:

@Provides
public XXInterface providesXX(XXImp imp) {
return imp;
}

其实这样的代码可以通过@Binds简化为

@Binds
public abstract XXInterface bindXX(XXImp imp);

同时你需要将你的Module改为abstract即可,但是要注意这两个不能共存于一个Module,是不是很简单.

Provider与Lazy

大家想必会使用过Lazy,很多语言都有Lazy,如最近大红大紫的Kotlin就可以通过by lazy {}来实现对象的延迟加载.

没错,Lazy<T>也是如此,只有当你调用get()时,才会真正注入.

Provider<T>与之的区别在于,Lazy延迟加载之后每次的调用都是同一个对象,而Provider则要看注入对象的实现,如果是通过@Scope约束的对象,则是同一个,否则每次都会创建新的.

依赖于包含

字面意思很好理解,翻译成Dagger的术语就是dependencysubcomponent.

下面分别举例子来说明其中的区别和使用方法.

Dependency

AnimalComponent

@Component(
dependencies = FoodComponent.class,
modules = AnimalModule.class
)
public interface AnimalComponent {
Animal getAnimal();
}

AnimalModule

@Module
class AnimalModule {
@Provides
public Animal providesAnimal(Food food) {
//Animal需要另外一个Component提供的Food来创建
return new Animal(food);
}
}

FoodComponent

@Component(modules = FoodModule.class)
public interface FoodComponent {
//这个是关键,必须显示指出可以提供Food对象的生成
Food getFood();
}

这样我们可以通过传入FoodComponent来完成注入, 如下:

DaggerAnimalComponent.builder().foodComponent(foodComponent).build().getAnimal()

Subcomponent

与依赖不同,Subcomponent拥有主Component所有注入对象,也就是说Subcomponent可以注入更多的对象, 通过生成代码也可以看出, 它的实现是主Component的内部类.

Cat

@Subcomponent(modules = {CatModule.class})
public interface CatComponent {
Cat getCat();
}

CatModule

@Module
public class CatModule {
@Provides
public Cat providesCat(Leg leg//Animal Component提供) {
return Cat(leg);
}
}

我们还必须在AnimalComponent显示提供CatComponent,因为如上所述,Cat是Animal的内部类了.

@Component(
dependencies = FoodComponent.class,
modules = AnimalModule.class
)
public interface AnimalComponent {
Animal getAnimal();
CatComponent createCatComponent();
}

这样我们就可以通过下面的办法来实现Cat的注入:

DaggerAnimalComponent.build().createCatComponent().getCat();

Subcomponent with explicit builder

当我们AnimalComponent需要对Cat进行修改再输出的话(如指定猫的名字),可能就需要为CatComponent提供Builder

@Subcomponent(modules = {CatModule.class})
public interface CatComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance Builder name(String name);
CatComponent build();
}
}

然后我们需要在AnimalModule里面使用这个Builder

@Module(subcomponents = CatComponent.class)//注意这里需要加上这一条声明
class AnimalModule {
@Provides
public Animal providesAnimal(Food food) {
//Animal需要另外一个Component提供的Food来创建
return new Animal(food);
}
@Provides
public CatComponent providesCatComponent(CatComponent.Builder builder) {
//这里只是举个例子,可能这里的Cat构造依赖于Animal的另外属性
return builder.name("喵喵").build();
}
}

Dagger.Android

平时我们注入的时候常常都是将Component存在Application里面,然后在Acitivity的onCreate或者Fragment的onAttach, 通过静态对象Applicate.component.inject(xxx)或者((XXApplication)getApplication()).getComponent().inject(xxx)来注入.

我们常常需要记住在固定的生命周期里面调用固定的语句,如果你的Activity或者Fragment在别的module里面使用公开的接口,对于Fragment你还可以对其对象进行注入(inject(fragmentInstance)),然而对Activity可能就没有很好的办法了...

Google的Dagger2提供了一套针对Android的东西,帮助你只需要调用AndroidInjection.inject(this)或者AndroidSupportInjection.inject(this)来注入,甚至还可以通过添加一些监听器,达到自动注入的效果哦.

下来看看如何实现吧

添加依赖

//x>=10
implementation 'com.google.dagger:dagger-android:2.x'
// if you use the support libraries
implementation 'com.google.dagger:dagger-android-support:2.x'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'

引入AndroidInjectionModule.class到你的Component

提供继承AndroidInjector<T>Subcomponent, 及其Builder

@Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}

通过Builder提供对应ActivityAndroidInjector.Factory

@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
@Binds
@IntoMap
@ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
} @Component(modules = {..., YourActivityModule.class})
interface YourApplicationComponent {}

Tips

类似于之前介绍Subcomponent.Builder, 如果你不需要定制该Builder, 如添加方法之类的, 那么这上面两步可以做简化.

@Module
abstract class YourActivityModule {
@ContributesAndroidInjector(modules = { /* modules to install into, like FragmentModule */ })
abstract YourActivity contributeYourActivityInjector();
}

参数module可以把想要注入的Fragment抽象到YourFragmentModule一并注入.

实现HasActivityInjector

public class YourApplication extends Application implements HasActivityInjector {
@Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector; @Override
public void onCreate() {
super.onCreate();
DaggerYourApplicationComponent.create()
.inject(this);
} @Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}

实际上所有的注入构造的工场方法AndroidInjector.Factory都被存入了一个Map保存在DispatchingAndroidInjector,我们可以查看其生成的代码,其中核心逻辑在maybeInject(T instance)

public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
try {
AndroidInjector<T> injector = factory.create(instance);
injector.inject(instance);
return true;
} catch (ClassCastException e) {
...
}
}

这就是其之所以能通过AndroidInjection.inject(this)实现注入的核心原理所在,Fragment同理.

当然你需要通过持有AndroidInjector Module的Component将这个DispatchingAndroidInjector注入了才行,一般可以在Application里面做.

实现自动注入.

由于使用Dagger.android扩展使注入入口得到统一,那么就可以通过添加监听的方式在activity与fragment创建的时候实现自动注入.

当然相信之后此部分代码可能会被融入进Dagger2.

Application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
handleActivity(activity);
}
....
} private static void handleActivity(Activity activity) {
if (activity instanceof HasSupportFragmentInjector) {
AndroidInjection.inject(activity);
}
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}

总结

Dagger2.Android2.10版本后推出,只有不到三个月,可见Dagger2还在不断自我强大的过程中,它的出现使得Android开发在很多层面变的简单,如果有希望进一步学习的朋友,可以参考官方文档和一些Google的Sample,会在最后的Reference给出连接.

Demo

Github

Reference

Dagger2进阶必备技能的更多相关文章

  1. Kiwi,BDD行为测试框架--iOS攻城狮进阶必备技能

    简介 Kiwi 是一个适用于iOS开发的行为驱动测试框架,旨在提供一个足够简单易用的BDD库. 项目主页: https://github.com/kiwi-bdd/Kiwi 示例下载: https:/ ...

  2. Android高工必备技能

    转载:http://www.jianshu.com/p/d791bbede02c Step 1. 玩转RxJava 使用RxJava处理异步极其方便,各种操作符可以对数据做流水线式操作,再加上与Ret ...

  3. 详解linux运维工程师入门级必备技能

    详解linux运维工程师入门级必备技能 | 浏览:659 | 更新:2013-12-24 23:23 | 标签:linux it自动化运维就是要很方便的运用各种工具进行管理维护,有效的实施服务器保护 ...

  4. 自动化部署必备技能—部署yum仓库、定制rpm包

    部署yum仓库.定制rpm包 目录 第1章 扩展 - yum缓存 1.1 yum缓存使用步骤... 1 1.1.1 导言... 1 1.1.2 修改配置文件... 1 1.1.3 使用缓存... 1 ...

  5. 【转帖】系统软件工程师必备技能-进程内存的working set size(WSS)测量

    系统软件工程师必备技能-进程内存的working set size(WSS)测量 2018年12月28日 18:43:01 Linuxer_ 阅读数:145 https://blog.csdn.net ...

  6. SQL Server管理员必备技能之性能优化

    SQL Server管理员必备技能之性能优化 高文龙关注1人评论1171人阅读2017-09-22 08:27:41 SQL Server 作为企业必不可少的服务之一,所以对于管理员的日常运维是一个极 ...

  7. 高级Linux运维工程师必备技能(扫盲篇)

    高级Linux运维工程师必备技能(扫盲篇) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在了解文件系统之前,我们要学习一下磁盘存储数据的方式,大家都知道文件从内存若要持久化存储的 ...

  8. 百度Hr分享,一个合格的数据工程师简历中必备技能?

    如果你是一名数据科学方面的求职者,你肯定想知道在简历上写些什么才能获得面试的机会:如果你想进入这个领域,你一定想知道具备哪些技术才能成为一名有竞争力的求职者. 在本文中,我们对Indeed中一千份数据 ...

  9. java高并发系列 - 第15天:JUC中的Semaphore,最简单的限流工具类,必备技能

    这是java高并发系列第15篇文章 Semaphore(信号量)为多线程协作提供了更为强大的控制方法,前面的文章中我们学了synchronized和重入锁ReentrantLock,这2种锁一次都只能 ...

随机推荐

  1. code forces 436 D. Make a Permutation!

    D. Make a Permutation! time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  2. code force 403B.B. The Meeting Place Cannot Be Changed

    B. The Meeting Place Cannot Be Changed time limit per test 5 seconds memory limit per test 256 megab ...

  3. c++学习笔记---03---从一个小程序说起2

    从一个小程序说起2 要求:编写一个程序,要求用户输入一串整数和任意数目的空格,这些整数必须位于同一行中,但允许出现在该行中的任何位置.当用户按下键盘上的"Enter"键时,数据输入 ...

  4. 开发中关于Git那些事

    如果你想精通Git,直接到 Git官网 把这本ProGit掌握已足以Pro Git 此文主要介绍一切开发中常用的git命令和一些配置技巧(诸如git别名配置,log打印技巧,版本回退以及分支管理等). ...

  5. docker下编译mangoszero WOW60级服务端(一)

    这几天看到暴雪准备开放怀旧服的新闻,突然想到几年前用大芒果window一键服务端自己搭建过服务,就想着在Linux环境下重新编译一套,毕竟Linux作为服务端,性能和稳定性都会高一些,于是在mac虚拟 ...

  6. WebSocket小插件

    一.WebSocket小介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...

  7. 数据库之Oracle——初级

    世上岂无千里马,人中难得九方皋: 酒船鱼网归来是,花落故溪深一篙. 关于数据库的第一篇博客,这是我的第二次,人生第二春,什么也不想说,静静的开始吧,至于为什么写唐诗,请看第一篇文章! Oracle 初 ...

  8. CLR之委托的揭秘(二)

    杂谈 在开始真正的代码之前,分析一下上周的一些工作内容,发现自己在代码上还是有很多小毛病需要纠正和去更改的,首先之前一直疏于文档的整理,几乎很少去写文档,第二对于接口开发过程中缺少一定的严谨性,很多问 ...

  9. Postman+newman+jenkins构建

      最近忙着项目接口测试,经过不同工具的对比,发现postman使用起来挺顺手的,所以马上决定使用这个工具进行接口测试工作.刚开始的时候,了解了下接口测试的相关信息,直接着手编写接口测试的测试用例信息 ...

  10. 重启mysql主从同步mongodb(tungsten-replicator)

    1. 连接mysql mysql -uroot -p;(mysql从库) 输入数据库密码 2. 停止主同步 mysql> stop slave; 3. 清数据 将mongo库数据清空 4. 杀主 ...