依赖注入这个模式(模式已经用烂了,这里再烂一次)是用来给应用的各部分解耦的。使应用开发更加可扩展,更容易维护。通过本文你会学到如何使用Dagger2来处理依赖。

简介

如果以对象需要另外的一个对象才能完成一个完整功能的话,那么这里就存在一个依赖。比如,悟空要用金箍棒才能三打白骨精,要筋斗云才能十万八千里。悟空有对金箍棒和筋斗云的依赖。你可以在悟空对象里初始化金箍棒,也可以用一个工厂方法批量生产金箍棒。使用依赖注入可以无需一个专门的类来初始化这些依赖对象。这样就实现了解耦。

本教程会使用最新的Dagger2(当前的版本是2.2)。这里是官网。你可以在这里找到最新的发布。

准备

Android Studio是必须的。其他:

1. Dagger2 基础

注解讲解:

  • @Module这个annotation修饰的类专门用来提供依赖
  • @Provides这个annotation修饰的方法用在Module类里
  • @Inject用来annotation一个依赖(可以是构造方法、field或者一般的方法)
  • @Component连接@Module和注入的桥梁

这些名词看起来非常抽象。下面稍微解释一下。依赖反射并没有什么神奇的地方。只不过是我们需要单独写初始化依赖的地方由其他的框架代替了。这个依赖关系也有我们常写的代码转移到了“配置文件”中。

在很久以前,依赖注入的框架就是这样处理依赖注入的:读取配置文件的依赖关系,然后用反射的方法初始化被依赖对象并赋值给调用依赖的对象。比如,我们之前在悟空类中初始化金箍棒:

public class Wukong {
private Jingubang jingubang; public Wukong(){
// 依赖
this.jingubang = Jingubang();
}
}

后来有了使用配置文件的依赖注入(这里都是虚构的文件格式):

<xml>
<com.xiyou.Wukong>
<dependency field="jingubang">
<com.xiyou.Jingubang />
</dependency>
</com.xiyou.Wukong>
</xml>

在悟空使用金箍棒的时候,依赖注入框架自动初始化好了金箍棒,并赋值给了悟空。

现在使用Dagger2。这里就有不得不说的牛X的地方了。因为是在Android里能用的资源没有后端那么多。尤其反射消耗比较大!所以Dagger为了满足移动开发节约资源的需要,没有使用反射实现依赖注入。而是在编译的时候同时生成依赖注入的相关代码。生成代码的根据就是前文中说明的那些注解(annotation)以及使用这些annotation的类、接口。

总结起来就一句话,Dagger把你需要在悟空类里写的金箍棒类的初始化代码都根据注解替你自动生成了!只不过这种生成的代码比明晃晃的使用new初始化的方法更加复杂一些。

Dagger2 开发步骤

把大象装冰箱一共分几步:

  1. 定义依赖和被依赖的对象的类,悟空类和金箍棒类。“悟空类”和“金箍棒类”的构造函数用@Inject注解修饰。
  2. 定义一个@Module注解的类,一般叫做XXXModule。里面写的@Provides注解修饰的方法。这些@Provides方法返回“悟空类”和“金箍棒类”对象。比如@Provides Wukong provideWukong(){ return new Wukong(); }
  3. 创建一个interface,并用@Component注解修饰。一般叫做XXXComponent。里面写一个注入方法:void inject(Wukong wk);。这里Wukong只是一个例子。任何你准备要注入的类都可以代替上面参数的Wukong类。
  4. 在需要注入的地方写@Inject的field。

最后,Dagger会根据上面的内容和最后的@Component接口生成一个DaggerXXXComponent的类型,使用这个类型来实现注入。上面的1到3步可以理解为依赖的配置。最后的XXXComponent代替古老的Reflect方式实现注入。

第一步的@Inject修饰的构造函数和`@Module`的`provideXXX`方法二者可以省略一个。
Dagger可以根据其中的任意一种配置创建依赖的对象。都写上等于有了双保险。

上文提到过多次。Dagger 2厉害的地方就在于这个库完全不用反射,而是用在编译期生成代码的方式实现的依赖注入。这个特点导致在Android Studio配置的时候需要做一些额外的工作。

这里假设你已经创建了一个新的Android应用项目。下面打开build.gradle文件,我们一步一步的来完成Dagger2的配置。

3. Android Studio的配置

第一步、

apply plugin: 'kotlin-android'             // 非必须
apply plugin: 'kotlin-android-extensions' // 必须!!!

为什么要加一个新的plugin呢?这个是为后面使用的kaptprovided提供支持的。gradle本身不支持这两个操作。

第二步、

buildscript {
ext.kotlin_version = '1.0.1-2'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
}

第三步、

dependencies {
// ...其他略...
compile 'com.google.dagger:dagger:2.2'
kapt 'com.google.dagger:dagger-compiler:2.2'
provided 'javax.annotation:jsr250-api:1.0'
}
  • dagger, 我们要用的正主。
  • dagger-compiler, 用来生成代码。
  • java.annotation, 提供Dagger意外的注解

最后,同步Gradle。

使用Dagger 2

下面就是Dagger一展身手的时候了。

首先,我们已经有悟空和金箍棒了。代码如下:

悟空:

import javax.inject.Inject;

/**
* Created by uncle_charlie on 6/4/2016.
*/
public class Wukong { @Inject
JinGuBang jinGuBang; @Inject
public Wukong() { } public String useJinGuBang() {
return this.jinGuBang.use();
}
}

金箍棒:

import javax.inject.Inject;

/**
* Created by uncle_charlie on 6/4/2016.
*/
public class JinGuBang { @Inject
public JinGuBang() { } public String use() {
return "user Jing gu bang";
}
}
  1. 悟空对金箍棒有依赖,所以金箍棒属性有@Inject注解修饰。
  2. 因为两个类都需要Dagger创建,所以在构造函数上都有@Inject注解。

第二步、创建@Module

创建@Module注解的类,并在其中添加@Provides注解修饰的方法。这些方法创建被依赖的对象。

import dagger.Module;
import dagger.Provides; /**
* Created by uncle_charlie on 6/4/2016.
*/
@Module
public class XiYouModule {
@Provides
// @Singleton
Wukong provideWukong() {
return new Wukong();
} @Provides
// @Singleton
JinGuBang provideJinGuBang() {
return new JinGuBang();
}
}
  • @Singleton注解表明,这个被依赖的对象在应用的生命周期里只有一个实例。
  • 这个里的@Provides方法和前一步说到的@Inject注解的构造函数两个可以只写一个。

第三步、@Component接口,连接@Module@Inject

@Module@Provides方法提供了被依赖的对象。@Inject@Component接口出现的地方则是指明了需要注入的地方(一般是一个field)。@Component接口就是用来把他们连接起来的。

import android.app.Activity;
import javax.inject.Singleton;
import dagger.Component; /**
* Created by uncle_charlie on 6/4/2016.
*/
@Component(modules = {XiYouModule.class})
@Singleton
public interface XiYouComponent {
void inject(Wukong wk);
void inject(Activity a);
}

其中inject()方法里使用的对象,就是包含@Injectfield的需要注入的对象。

在这个接口中也可以不用inject()方法,而使用provideXXX()方法后面会有更多介绍。

注意:@Component接口一定要在直接中指明@Module类型

第四步、使用@Component接口获取对象

经过前面的步骤,依赖和被依赖对象关系都已经配置好了。下面就来获取被依赖对象来注入依赖对象。

public class MainActivity extends AppCompatActivity {
private static final String TAG = "##MainActivity"; @Inject
Wukong wukong; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); TextView welcomeTextView = (TextView) findViewById(R.id.welcome_textview);
// 1
XiYouComponent xiYouComponent = DaggerXiYouComponent
.builder()
// 2
.xiYouModule(new XiYouModule())
.build();
xiYouComponent.inject(this);
// 3
welcomeTextView.setText(wukong.useJinGuBang());
}
}

首先主要到属性@Inject Wukong wukong;已经在MainActivity 声明了。这里表明一个依赖关系:这个activity依赖于悟空,并准备注入悟空对象。

  1. Dagger2会在编译器自动生成依赖注入的代码,所以在添加上面的代码之前需要编译一下。DaggerXiYouComponent就是Dagger根据我们的XiYouModule类生成的代码。
  2. 在这一步给DaggerXiYouComponentbuilder添加XiYouModule的实例。如果这个Module只需要用到无参构造函数的话可以用一种省略用法:create()方法。可以简写为:
DaggerXiYouComponent
.builder()
// 2
//.xiYouModule(new XiYouModule())
//.build()
.create();
  1. Component接口的对象调用inject(this)方法之后注入即完成。所以可以直接使用@Inject Wukong wukong;属性来调用方法:welcomeTextView.setText(wukong.useJinGuBang());最后在activity中显示方法返回的文字。

运行代码,看看结果吧。

结论

以上内容可以概括为:什么被依赖,就把什么放在@Module类里(或者什么被依赖,就给什么添加@Inject的无参构造函数)。什么有依赖(@Inject属性),就把什么放在@Component接口的inject()方法参数里。(或者有什么@Inject属性,就在@Component接口里provide什么对象)。这个概括不一定严密,但是基本用法全部包括了。

依赖注入是很有用的。以上的内容只是Dagger2依赖注入的一部分。各位读者还需要根据官方文档多加练习才能更好的理解依赖注入和Dagger的各种用法。

用Dagger2在Android中实现依赖注入的更多相关文章

  1. 浅析android中的依赖注入

    这几年针对Android推出了不少View注入框架,例如ButterKnife.我们首先来了解一下使用这些框架有什么好处,其实好处很明显:它可以减少大量的findViewById以及setOnClic ...

  2. Andriod中的依赖注入

    Web后端开发者应该对依赖注入都比较熟悉,至于Android又是如何进行依赖注入的呢?在这篇文章中,让我们一起通过一个例子了解一下在Android中进行依赖注入的好处. AndroidAnnotati ...

  3. Android 和 Dagger 2 中的依赖注入

    原文:Dependency Injection in Android with Dagger 2 作者:Joe Howard 译者:kmyhy 在现代开发团队中到处充斥着"你一定要用依赖注入 ...

  4. ASP.NET Core 中文文档 第四章 MVC(3.8)视图中的依赖注入

    原文:Dependency injection into views 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:孟帅洋(书缘) ASP.NET Core 支持在视图中使用 依赖 ...

  5. 在WPF中使用依赖注入的方式创建视图

    在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...

  6. ASP.NET Core 在 JSON 文件中配置依赖注入

    前言 在上一篇文章中写了如何在MVC中配置全局路由前缀,今天给大家介绍一下如何在在 json 文件中配置依赖注入. 在以前的 ASP.NET 4+ (MVC,Web Api,Owin,SingalR等 ...

  7. 在.NET Core控制台程序中使用依赖注入

    之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...

  8. ASP.NET Core中的依赖注入(1):控制反转(IoC)

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

  9. ASP.NET Core中的依赖注入(2):依赖注入(DI)

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

随机推荐

  1. C#中委托

    委托是一种安全地封装方法的类型,它与 C 和 C++ 中的函数指针类似.与 C 中的函数指针不同,委托是面向对象的.类型安全的和保险的.一个委托类型是代表与特定参数列表和返回类型的方法的引用类型.实例 ...

  2. spyder在编辑过程中被自己弄乱了,想要恢复成安装时默认的格式或者重置页面格式的解决办法

    打开spyder,tools-->Reset Spyder to factory defaults,按照如上操作即可恢复成安装时的默认格式.

  3. Vs2015 c# 诊断工具查看程序的占用情况

    windbg用着还不熟悉,dottrace  还要版权,着急查看程序的cpu 的使用情况,因为程序开启之后占用处理器资源较大,问题在哪里呢,于是点开了vs2015自带的诊断工具,以前偶尔打开过,没发现 ...

  4. 796B Find The Bone

    B. Find The Bone time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...

  5. C#泛型的学习

    编码: class Program { static void Main(string[] args) { ; Test<int> test1 = new Test<int>( ...

  6. oracle表或视图不存在和标识符无效的问题解决

    通过dbvisualizer修改表的名字时,一定要改成大写,否则会报错,数据库中没有该表的错误. 如果新的表名为小写,不行! select的时候,可以用小写名. 标识符无效解决:字段名默认都是大写的, ...

  7. python 字符串占位符的使用

    name2='我是{} 我的专业是 {}'.format('张三','计算机科学技术')print(name2)

  8. 为什么要用jvm .

    挚享科技 2018.4.8 运行java程序字节码,实现跨平台.. Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成 在Java虚拟机上运行的目标代码(字节码 ...

  9. Java SE学习【二】——面向对象

    面向对象的学习也进行了一段时间,这段时间学了,类和对象:属性:方法:封装:继承:多态:接口.也算是有一些自己的理解,不愧是贴近人类思维的思想,老师讲时我常常会想到以前的一些事物和其交相印证,其中最常想 ...

  10. 验证签名机制——java示例

    简单的验证公钥私钥签名认证: 公钥是对外公开的部分,私钥是不公开的部分,一般在项目开发中公钥是给用户,私钥是存于服务器上,二者中有一个加密,则需要另外一个来解密. 下面是java实现的一个比较简单的示 ...