Dagger2进阶必备技能
之前写过一篇文章介绍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的术语就是dependency
与subcomponent
.
下面分别举例子来说明其中的区别和使用方法.
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
提供对应Activity
的AndroidInjector.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.Android
自2.10
版本后推出,只有不到三个月,可见Dagger2
还在不断自我强大的过程中,它的出现使得Android
开发在很多层面变的简单,如果有希望进一步学习的朋友,可以参考官方文档和一些Google的Sample,会在最后的Reference
给出连接.
Demo
Reference
- https://google.github.io/dagger/android.html
- https://google.github.io/dagger/subcomponents.html
- https://github.com/googlesamples/android-architecture-components
Dagger2进阶必备技能的更多相关文章
- Kiwi,BDD行为测试框架--iOS攻城狮进阶必备技能
简介 Kiwi 是一个适用于iOS开发的行为驱动测试框架,旨在提供一个足够简单易用的BDD库. 项目主页: https://github.com/kiwi-bdd/Kiwi 示例下载: https:/ ...
- Android高工必备技能
转载:http://www.jianshu.com/p/d791bbede02c Step 1. 玩转RxJava 使用RxJava处理异步极其方便,各种操作符可以对数据做流水线式操作,再加上与Ret ...
- 详解linux运维工程师入门级必备技能
详解linux运维工程师入门级必备技能 | 浏览:659 | 更新:2013-12-24 23:23 | 标签:linux it自动化运维就是要很方便的运用各种工具进行管理维护,有效的实施服务器保护 ...
- 自动化部署必备技能—部署yum仓库、定制rpm包
部署yum仓库.定制rpm包 目录 第1章 扩展 - yum缓存 1.1 yum缓存使用步骤... 1 1.1.1 导言... 1 1.1.2 修改配置文件... 1 1.1.3 使用缓存... 1 ...
- 【转帖】系统软件工程师必备技能-进程内存的working set size(WSS)测量
系统软件工程师必备技能-进程内存的working set size(WSS)测量 2018年12月28日 18:43:01 Linuxer_ 阅读数:145 https://blog.csdn.net ...
- SQL Server管理员必备技能之性能优化
SQL Server管理员必备技能之性能优化 高文龙关注1人评论1171人阅读2017-09-22 08:27:41 SQL Server 作为企业必不可少的服务之一,所以对于管理员的日常运维是一个极 ...
- 高级Linux运维工程师必备技能(扫盲篇)
高级Linux运维工程师必备技能(扫盲篇) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在了解文件系统之前,我们要学习一下磁盘存储数据的方式,大家都知道文件从内存若要持久化存储的 ...
- 百度Hr分享,一个合格的数据工程师简历中必备技能?
如果你是一名数据科学方面的求职者,你肯定想知道在简历上写些什么才能获得面试的机会:如果你想进入这个领域,你一定想知道具备哪些技术才能成为一名有竞争力的求职者. 在本文中,我们对Indeed中一千份数据 ...
- java高并发系列 - 第15天:JUC中的Semaphore,最简单的限流工具类,必备技能
这是java高并发系列第15篇文章 Semaphore(信号量)为多线程协作提供了更为强大的控制方法,前面的文章中我们学了synchronized和重入锁ReentrantLock,这2种锁一次都只能 ...
随机推荐
- ng-options指令语法
ng-options一般有以下用法 对于数组: label for value in array select as label for value in array label group by g ...
- java 随记
后台开发的过程中积累的关于java的杂记 架构 SSH框架 为什么要分层? 因为分层使代码变得清晰,容易写也容易阅读,更重要的是让代码扩展性更好,层与层之间的改动不会互相影响 各层的分工 dao--与 ...
- 0_Simple__matrixMul + 0_Simple__matrixMul_nvrtc
矩阵乘法,使用一维线程块和共享内存.并且在静态代码和运行时编译两种条件下使用. ▶ 源代码:静态使用 #include <stdio.h> #include <assert.h> ...
- 初学者最易懂的git教程在这里!
一.git简介: Linux创建了Linux,但是Linux的发展壮大是由世界各地的热心志愿者参与编写的?那么那么多份的代码是怎么合并的呢?之前是在2002年以前,世界各地的志愿者把源代码文件通过di ...
- [java基础] java中的自动装箱与自动拆箱
自动装箱的一个例子: Integer i = 1; //实际上是执行了Integer i = Integer.valueOf(1) 自动拆箱的一个例子: Integer a =1; int b = a ...
- 06.十分钟学会表达式语言EL
一. 概念:MVC设计模式一个主要好处就是让jsp中的代码越来越来少,而且规定只能出现三种代码:接收属性,判断语句,迭代输出.但是,在开发中,jsp输出至少还是需要接受VO对象的,这时候为了避免导入V ...
- Layui框架+PHP打造个人简易版网盘系统
网盘系统 大家应该都会注册过致命的一些网盘~如百度云.百科介绍:网盘,又称网络U盘.网络硬盘,是由互联网公司推出的在线存储服务,服务器机房为用户划分一定的磁盘空间,为用户免费或收费提供文件的存储. ...
- java的配置环境简介
============================================================================== 学java对很多人来说并不陌生,听的最多的 ...
- SpringAware
哈哈,终于把分布式的课程演讲给混过去了,下面开始随便自己学点东西. 正题:SpringAware--------在实际项目中,用到spring容器的本省功能资源,这是Bean必须意识到Spring容器 ...
- key-value存储数据库--Redis
1.简介 Redis是完全开源的ANSI C语言编写.遵守BSD协议,高性能的key-value数据库. 1.1特点 Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载 ...