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

AndroidDagger2从零单排(一)基础注解

  转发请注明出处:https:www.jianshu.comp7ee1a1100fab  Dagger2作为Android界最具杀伤力的匕首,本系列文章将用最通俗的语言带领你揭开

  转发请注明出处:https://www.jianshu.com/p/7ee1a1100fab
  Dagger2作为Android界最具杀伤力的匕首,本系列文章将用最通俗的语言带领你揭开它的真面目。
  边缘OB:从零单排带你从低分局打到高分局,从First Blood(第一滴血)到Holy Shit(超越神的杀戮),每盘Rampage(暴走)不在话下!
  Android Dagger2 从零单排(一) 基础注解
  Android Dagger2 从零单排(二) @Qualifier
  Android Dagger2 从零单排(三) @Scope
  Android Dagger2 从零单排(四) Dependencies与SubComponent

1.Dagger2简介

  ”A fast dependency injector for Android and Java.”
  Dagger2GitHub地址的首页简介就这么一句话,Android和java的快速依赖注入。简单述说就是组件只管依赖的使用,而依赖的具体实现交给容器去决定,这是DI(Dependency Injection)框架的核心思想。另外使用Dagger2不用担心性能的消耗,不使用反射,所有的注解均停留在编译时期。

2.配置Dagger2:

dependencies {
api 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}

  注意:如果gradle版本低于2.2,需要使用apt插件,Kotlin写法也有点。

3.注解的分类:

  在描述注解的作用前,我们先看dagger2最简单的例子,
  例子一,假设领导下午要出去视察民情,需要一辆公交车,司机就不用请了,领导做表率自己开,小秘把领导的指示告诉了车场调度员。
  找辆公交车要停放在停车场(Bus要注入到ParkingActivity),停车场不管公交车按什么路线停车(ParkingActivity不管Bus是如何注入的),车场调度员会负责好管理(Dagger2容器会将Bus的注入到ParkingActivity)。
  DaggerParkingComponent类是编译过后Dagger2自动生成的,是ParkingComponent的实现类,可以说DaggerParkingComponent就是实际的车场调度员,ParkingComponent是对车场调度员的约束,在onCreate方法完成了注入过程。

public class Bus {
@Inject
public Bus() {
}
}
public class ParkingActivity extends Activity {
@Inject
Bus mBus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
DaggerParkingComponent.create().inject(this);//DaggerParkingComponent类需要编译才会生成
((TextView) findViewById(R.id.text)).setText(mBus.toString());
}
}
@Component
public interface ParkingComponent {
void inject(ParkingActivity activity);
}

@Inject注解:
  (1)注解在属性中表示该属性需要依赖注入,不能使用private修饰,示例代码表示需要注入属性mBus(Bus的车位):

@Inject
Bus mBus;

  (2)注解在方法中表示该方法需要依赖注入,不能是抽象方法,不能使用private修饰,示例代码表示需要注入方法injectBus:

//@Inject
Bus mBus;
@Inject
public void injectBus(Bus bus) {
mBus = bus;
}

  方法注入的参数同样由Dagger2容器提供,以上代码的目的与第一点介绍的属性注入一样,都是为了注入mBus,如果目的是注入属性的话,方法注入和属性注入基本没有区别,属性注入是Dagger2中使用最多的一个注入方式。
  那么什么情况下应该使用方法注入?比如依赖需要this对象的时候,方法注入可以提供安全的this对象。
  注意,Dagger2容器先调用属性注入,然后再方法注入,若把示例代码mBus的@Inject取消注释,此时mBus会注入2次,并且两次注入的Bus也不相同。
  (3)注解在构造方法中表示此类能为Dagger2提供依赖关系,Dagger2可以使用这个构造方法构建对象(Bus的来历):

@Inject
public Bus() {
}

  如果有多个构造函数,只能注解一个,否则编译报错,多构造方法的方式下一篇会介绍。
@Component注解:
  一般用来注解接口,被注解的接口在编译时会生成相应的实例,实例名称一般以Dagger为前缀,作为所需注入依赖(ParkingActivity的mBus属性)和提供依赖(Bus类构造方法)之间的桥梁,把提供的依赖(Bus)注入到所需注入的依赖中(ParkingActivity的mBus属性)。
  通俗说,就是Dagger2的容器,在例子中是车场调度员,把公交车和停车场联系在一起。车场调度员知道停车场要准备一辆公交车,停车场不需要知道车是哪里来的,也不需要知道怎么停,车场调度员找好车后,停在车位上就完事,等候领导用车就可以了。
  例子一总结,两个@Inject注解形成了依赖关系,@Component作为连接这个关系的桥梁存在,寻找到依赖并且注入,并且注入与被注入之间互不干涉,经过编译@Component生成Dagger为前缀的实例,调用实例的方法注入。

  例子二,领导想着不妥,自己没开过公交车,为保险起见还是赶紧吩咐小秘,去给配个公交车司机,小秘自然跟车场调度员说了。
  车场调度员一想,只是比之前多了一个步骤,给公交车配置一个司机(Bus类添加String类型Driver构造方法)。于是打电话给开公交车的隔壁老王,老王自然是应诺了下来,于是乎,对例子一的代码做一下修改。

public class Bus {
private String driver;
@Inject
public Bus(String driver) {
this.driver = driver;
}
}
public class ParkingActivity extends Activity {
@Inject
Bus mBus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger);
DaggerParkingComponent.create().inject(this);//DaggerParkingComponent类需要编译才会生成
((TextView) findViewById(R.id.text)).setText(mBus.toString());//重写Bus的toString()方法能看到打印出"隔壁老王",注入成功
}
}
@Component(modules = ParkingModule.class)
public interface ParkingComponent {
void inject(ParkingActivity activity);
}
@Module
public class ParkingModule {
public ParkingModule() {
}
@Provides
public String provideDriver() {
return "隔壁老王";
}
}

@Module注解:
  该注解与@Provides结合为Dagger2提供依赖关系,对上文@Inject第三点的补充,用于不能用@Inject提供依赖的地方,如第三方库提供的类,基本数据类型等不能修改源码的情况。
@Provides注解:
  @Provides仅能注解方法,且方法所在类要有@Module注解。注解后的方法表示Dagger2能用该方法实例对象提供依赖。按照惯例,@Provides方法的命名以provide为前缀,方便阅读管理。
  例子二总结,首先@Component注解包含了一个ParkingModule类,表示Dagger2可以从ParkingModule类查找依赖,Dagger2会自动查找ParkingModule类有@Provides注释的方法实例依赖,最后完成注入
  注意1,如果在ParkingModule里面同样提供Bus的依赖,Dagger2会优先在@Module注解的类上查找依赖,没有的情况才会去查询类的@Inject构造方法,如下面的代码,则Bus的司机就是小王而不是老王了。

@Module
public class ParkingModule {
public ParkingModule() {
}
@Provides
public String provideDriver() {
return "隔壁老王";
}
@Provides
public Bus provideBus() {
return new Bus("楼上小王");
}
}

  注意2,@Module类可以从构造方法传入依赖,@Provides方法也可以有依赖关系。
  @Provides方法也有依赖关系的情况,Dagger2会继续查找可以提供依赖的方法,类似于一种递归的状态,一步一步返回实例。如下代码,ParkingModule构造方法传入driver为provideDriver方法提供依赖返回,provideDriver返回driver作为provideBus方法的依赖实例Bus。
  ParkingModule加入有参构造方法后,调用方式也需要变成,现在司机就变成了楼下老李了。

@Module
public class ParkingModule {
private String driver;
public ParkingModule(String driver) {
this.driver = driver;
}
@Provides
public String provideDriver() {
return driver;
}
@Provides
public Bus provideBus(String driver) {
return new Bus(driver);
}
}
//调用方式改变
DaggerParkingComponent.builder().parkingModule(new ParkingModule("楼下老李")).build().inject(this);

关于注入方法的疑惑:
  初次接触Dagger2时,会发现每次Build之后,create()方法时有时无,其实create()方法是简化注入流程而设计的,点进去看源码实际上是在内部帮你调用了。

public static MainComponent create() {
return new Builder().build();
}

以下情况都会生成create()方法:
  (1)@Component注解接口没有声明任何Module,这种情况自然不必多说。
  (2)@Component注解接口声明了Module,没有使用Module提供的依赖,无论Module构造方法是否有参,这种情况@Component注解生成的Java文件也有提示:

/**大约意思就是你声明了Module,但component没有用到里面的实例,实例Module是多余无用的操作。
* @deprecated This module is declared, but an instance is not used in the component. This
* method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/

  (3)@Component注解接口声明了Module,使用了Module提供的依赖,Module构造方法没有参数,点进去看源码实际上是在内部初始化了Module。

public MainComponent build() {
if (mainModule == null) {
this.mainModule = new MainModule();
}
return new DaggerMainComponent(this);
}

下面这种是一定不会有create()方法出现:
  (1)@Component注解接口声明了Module,使用了Module提供的依赖,Module构造方法有参数,无论你是否使用了该参数作为依赖,这时候注入都需要初始化Module才能注入成功,看源码如果不初始化Module会直接报异常。

public MainComponent build() {
if (mainModule == null) {
throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}

  开始接触时create()方法可以作为检测手段,例如声明了Module,且Module构造方法有参数,如果有create()方法,表明此时没使用Module提供的依赖,可以在Module查找下原因。实际项目运用中,create()方法比较少使用,一般Module都会传参。

最后总结了一下依赖注入流程:
1:查找Module中是否有该实例的@Provides方法。
  1.1:有,走第2点。
  1.2:没有,查找该实例是否有@Inject构造方法。
    1.1.1:有,走第3点。
    1.1.2:没有,注入失败。
2:@Provides方法是否有参数
  2.1:有,则回到第1点查找每个参数的依赖
  2.2:没有,实例该类返回一次依赖
3:@Inject构造方法是否有参数
  3.1:有,则回到第1点查找每个参数的依赖
  3.2:没有,实例该类返回一次依赖
4:以上流程递归返回注入目标的所有依赖,最后依赖注入。

  Demo源码截我 对应daggerOne包名
  Dagger2 GitHub地址
  Dagger2 官网地址
  所有的测试实例均基于2.15版本。
  下一篇,我们来研究多构造方法与多个@Provides方法返回同一数据类型的情况:@Qualifier注解。


推荐阅读
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了一款名为TimeSelector的Android日期时间选择器,采用了Material Design风格,可以在Android Studio中通过gradle添加依赖来使用,也可以在Eclipse中下载源码使用。文章详细介绍了TimeSelector的构造方法和参数说明,以及如何使用回调函数来处理选取时间后的操作。同时还提供了示例代码和可选的起始时间和结束时间设置。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
author-avatar
特异型爷们喵
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有