Dagger2是一个Java和Android的依赖注入框架.

本文介绍Android中dagger2的基本使用.

其中包括@Inject, @Component, @Module@Provides注解的使用.

使用依赖注入的好处

1.使用类和被依赖的对象构造分开,这样如果我们需要改变被依赖类的构造方法,不必改动每一个使用类.

2.对各种被依赖类的实例,可以只构造一次.

3.当我们需要更换一种实现时,只需要保证接口一致.

4.利于单元测试,我们可以方便地mock依赖类的对象.

优点总结: 创建对象和使用对象分离, 模块化增强.

Dagger2的使用

Set Up

在项目的build.gradle里加这个:

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

然后app的build.gradle:

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt' android {
compileSdkVersion 24
buildToolsVersion "24.0.0" defaultConfig {
applicationId "com.ddmeng.dagger2sample"
minSdkVersion 16
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
} dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'javax.annotation:jsr250-api:1.0'
compile 'com.google.dagger:dagger:2.2'
apt 'com.google.dagger:dagger-compiler:2.2'
}

常用注解

最常使用的主要是以下这几个注解:

@Component

Annotates an interface or abstract class for which a fully-formed, dependency-injected implementation is to be generated from a set of modules(). The generated class will have the name of the type annotated with @Component prepended with Dagger. For example, @Component interface MyComponent {...} will produce an implementation named DaggerMyComponent.

@Module

Annotates a class that contributes to the object graph.

@Inject

Dagger constructs instances of your application classes and satisfies their dependencies. It uses the javax.inject.Inject annotation to identify which constructors and fields it is interested in.

Use @Inject to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.

@Provides

Annotates methods of a module to create a provider method binding. The method's return type is bound to its returned value. The component implementation will pass dependencies to the method as parameters.

Dagger2基本使用

最简单的一个实例

首先写一个Component

@Component(modules = MyApplicationModule.class)
public interface MyApplicationComponent {
// this should be an interface or abstract class // write like this, and Make Project, then a DaggerMyApplicationComponent class will be generated
}

此时里面的Module内容可以暂时为空:

@Module
public class MyApplicationModule {
}

写好后make一下,就生成了

package com.ddmeng.dagger2sample.component;

import com.ddmeng.dagger2sample.module.MyApplicationModule;
import dagger.internal.Preconditions;
import javax.annotation.Generated; @Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class DaggerMyApplicationComponent implements MyApplicationComponent {
private DaggerMyApplicationComponent(Builder builder) {
assert builder != null;
} public static Builder builder() {
return new Builder();
} public static MyApplicationComponent create() {
return builder().build();
} public static final class Builder {
private Builder() {} public MyApplicationComponent build() {
return new DaggerMyApplicationComponent(this);
} /**
* @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.
*/
@Deprecated
public Builder myApplicationModule(MyApplicationModule myApplicationModule) {
Preconditions.checkNotNull(myApplicationModule);
return this;
}
}
}

需要切换到project视图下才能看见.

生成的这个实现,名字是在我们自己的Component名前面加了Dagger.

如果我们的类名不是顶级的,即还有外部类,则会以下划线分隔连接.

现在我们的生成的myApplicationModule()方法被标记为@Deprecated,这是因为我的module里面什么都还没有呢,所以被认为是没有必要的.

现在我们添加一个要用的LogUtils类. 想要在MainActivity里面用.

写好LogUtils类,在构造函数上标记@Inject. 这时候就将LogUtils加入了dependency graph中, 相当于作为预备队员.

想要在MainActivity作为一个字段用,

在Component里面写一句:

void inject(MainActivity activity);

因为此时还是没有用到Module,所以在application里面可以直接build,保存component:

public class SampleApplication extends Application {

    private MyApplicationComponent component;

    @Override
public void onCreate() {
super.onCreate();
component = DaggerMyApplicationComponent.builder().build(); } public MyApplicationComponent getComponent() {
return component;
}
}

在MainActivity使用的时候, 先get到Component, 然后调用inject()方法, 字段就被注入了.

public class MainActivity extends AppCompatActivity {

    @Inject
LogUtils logUtils; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ((SampleApplication) getApplication()).getComponent().inject(this);
logUtils.i("tag", "hi, I'm an instance of LogUtils");
}
}

运行程序后可以看到打出log,证明注入成功.

此时我们看到生成的代码有三个类:

public final class DaggerMyApplicationComponent implements MyApplicationComponent {
public enum LogUtils_Factory implements Factory<LogUtils> {
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {

可以通过查看调用栈来看调用关系.

单例@Singleton

如果我们想让工具类是单例,只需要在上面的基础上,在类名前加上@Singleton.

此时对应的Component也需要加上@Singleton.否则编译会不通过.

加好之后,可以打印hashCode()看出, 标记了@Singleton的这个对象,不论被注入几次,都是同一个对象.

在我们的例子中, 可以让FileUtils作为一个单例被注入:

@Singleton
public class FileUtils { @Inject
public FileUtils() {
Log.i(LogUtils.TAG, "new FileUtils: " + hashCode());
} public void doSomething() {
Log.i(LogUtils.TAG, "do sth with FileUtils " + hashCode());
}
}

查看生成的代码,可以看见DaggerMyApplicationComponent为单例的类多保存了一个字段:

private Provider<FileUtils> fileUtilsProvider;

它在init的时候被初始化为:

this.fileUtilsProvider = ScopedProvider.create(FileUtils_Factory.create());

包了一层之后,在ScopeProvider里实现了单例:

  @Override
public T get() {
// double-check idiom from EJ2: Item 71
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = factory.get();
}
}
}
return (T) result;
}

@Module@Provides的使用

上面的注入都是用@Inject, 在构造函数和要使用的字段上标记.

有些情况下@Inject是不能满足需求的.

But @Inject doesn’t work everywhere.

  1. Interfaces can’t be constructed. 接口类型不能直接被构造.
  2. Third-party classes can’t be annotated. 第三方的类不能改动它的代码.
  3. Configurable objects must be configured! 需要配置的对象需要被配置.

这些情况下, @Inject不够用啦, 这时候就要用@Provides标记的方法.

方法的返回值返回了它满足的依赖, 它实际返回的对象可以是返回值接口的实现,或者是返回值类型的子类.

@Provides方法也可以有依赖, 即它的参数.

Dagger会注入它的参数值, 如果它的参数值不能被注入, 则编译会失败.

注意这个寻找参数注入的过程是在@Component级别的, 只要这个Component里面有这个参数类型的注入, 即便可能是在另一个Module, 就会自动采用.

所有的@Provides方法都需要放在@Module里面.

按照命名习惯(By convention), 一般@Provides标记的方法都有一个provide前缀, 而module类都有一个Module后缀.

例子:

@Module
public class MyApplicationModule { private Context context; public MyApplicationModule(Context context) {
this.context = context;
} @Provides
@Singleton
public Context providesContext() {
return context;
} // Inject interface, return implementation class instance
@Provides
public HttpUtil provideHttpUtil() {
Log.i(LogUtils.TAG, "provideHttpUtil");
return new MyHttpUtil();
} // Inject class from third-party, or Android framework service
// This provide method need a parameter, Dagger will obtain the parameter value (injected it)
// If the parameter is not injectable, then compilation failed
@Provides
@Singleton
ConnectivityManager provideConnectivityManager(Context context) {
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
}

Dagger2中几种常用注解总结

@Inject

@Inject的用法分为三种

  • 构造函数上的@Inject:

如果构造函数是有参数的, 则它的所有参数都会自动从dependency graph中找到并注入.

同时构造的这个类也被作为dependency graph的一部分.

但是我们在一个类中最多只能用@Inject标记一个构造方法.

  • 字段上的@Inject: 从dependency graph中找到并注入字段.

这里需要手动调用

(SampleApplication) getApplication()).getComponent().inject(this);

类似的方法, 在这个方法被调用之前, 字段都是null.

注意这里的字段不能是private的.

  • public方法上的@Inject:

所有方法的参数都会由dependency graph提供.

方法注入在构造函数之后立即调用, 意味着我们可以用一个构建好的this对象.

@Module@Provides

@Module标记了提供依赖的类, 其中包含了一些@Provides标注的方法, 返回值即依赖.

@Component

@Component标记的接口负责将所有的事情联系起来, 可以看做是@Module@Inject之间的桥梁.

我们可以定义我们用的依赖来自哪些Module或者Component.

在Component里可以定义哪些依赖是公有的 (提供返回值为某种依赖的无参数方法) , 也可以定义我们的component可以去哪里inject对象 (void inject()方法, 参数是去注入的地方) .

@Component可以有自己的子Component, 也可以有lifecycle.

先就这么多吧, 更多更高级的使用可以期待下文, 也可以参见后面的参考资料.

本文地址: Using Dagger2 in Android

本文Demo: dagger2-sample

参考资料:

dagger2 repo

dagger2 website

User Guide

Dagger 2.0文档

dagger 2的sample

这里有些guides:

Code Path Guides: DI with dagger2

Dagger2

这里有一系列关于Dagger2的文章还挺好的:

Froger_mcs dev blog

dagger 1 to dagger 2 migration

Introduction to DI

Dagger2 API

Inject everything - ViewHolder and Dagger2 (with Multibinding and AutoFactory example)

Custom Scope

作者的例子:

Github Client

Using Dagger2 in Android的更多相关文章

  1. 听说你还不会用Dagger2?Dagger2 For Android最佳实践教程

    前言 Dagger2是现在非常火的一个依赖注入框架,目前由Google维护,在Github上面已经有12K star了.Dagger2的入门门槛其实是比较高的,据了解,目前有很多Android工程师对 ...

  2. Dagger2在Android开发中的应用

    世界是普遍联系的,任何事物和个体都直接或间接相互依赖,在时空长河中共同发展.在面向对象的世界中,更是如此,类与类之间的依赖,关联关系,模块(亦或是分层架构中的层)之间的耦合关系,都是我们在软件开发实践 ...

  3. 用Dagger2在Android中实现依赖注入

    依赖注入这个模式(模式已经用烂了,这里再烂一次)是用来给应用的各部分解耦的.使应用开发更加可扩展,更容易维护.通过本文你会学到如何使用Dagger2来处理依赖. 简介 如果以对象需要另外的一个对象才能 ...

  4. Android注解使用之Dagger2实现项目依赖关系解耦

    前言: 最近牵头发起公司app的重构工作,如何通过重构让项目的耦合降低.开发效率提高,一直是我努力的方向,今天来学习一下一个注解框架Dagger2,然后看看如何使用它来降低项目的耦合. Dagger2 ...

  5. Android技术框架——Dagger2

    Dagger2 是一个Android依赖注入框架.没错依赖注入,学习过spring的同学看到这词,应该是挺熟悉的.当然既然是Android的课题,我们就来聊聊Dagger2 ,android开发当前非 ...

  6. Android Weekly Notes Issue #223

    Android Weekly Issue #223 September 18th, 2016 Android Weekly Issue #223 本期内容包括: Offline时间戳处理; Acces ...

  7. Android Weekly Notes Issue #219

    Android Weekly Issue #219 August 21st, 2016 Android Weekly Issue #219 ARTICLES & TUTORIALS Andro ...

  8. 15 个 Android 通用流行框架大全(转)

    1. 缓存 DiskLruCache    Java实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 Picas ...

  9. Android 通用流行框架

    原文出处: http://android.jobbole.com/83028/ 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Andro ...

随机推荐

  1. 用Taurus.MVC 做个企业站(下)

    前言: 上一篇完成了首页,这一篇就把剩下的几个功能给作了吧. 包括文章列表.文章详情和产品展示. 1:文章列表: 原来的ArticleList.aspx 1:现在的articlelist.html 除 ...

  2. 我正在使用Xamarin的跨平台框架—Xamarin.Android回忆录

    一.缘起 在自己给别家公司做兼职外包的时候,已经明确知道外包的活不是那么好干的,一般在经历了初期热血澎湃的激情后,逐渐冷淡,愤怒,再冷淡,再愤怒…,听上去好像高潮迭起,但令人尴尬的是,这高潮迭起我们都 ...

  3. JAVA通信系列一:Java Socket技术总结

    本文是学习java Socket整理的资料,供参考. 1       Socket通信原理 1.1     ISO七层模型 1.2     TCP/IP五层模型 应用层相当于OSI中的会话层,表示层, ...

  4. 堆排序与优先队列——算法导论(7)

    1. 预备知识 (1) 基本概念     如图,(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组 ...

  5. Git(进击学习:远程仓库操作)-V3.0

    1.查看当前的远程仓库:git remote或git remote -v 2.添加远程仓库:git remote add [shortname] [url] git remote add pb git ...

  6. iOS APP 如何做才安全

    本来 写了一篇<iOS 如何做才安全--逆向工程 - Reveal.IDA.Hopper.https抓包 等>,发现文章有点杂,并且“iOS 如何做才安全”这部分写的越来越多,觉得 分出来 ...

  7. MVC5 网站开发之四 业务逻辑层的架构和基本功能

    业务逻辑层在Ninesky.Core中实现,主要功能封装一些方法通过调用数据存储层,向界面层提供服务.   目录 奔跑吧,代码小哥! MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 ...

  8. AngularJS 脏检查深入分析

    写在开头 关于Angular脏检查,之前没有仔细学习,只是旁听道说,Angular 会定时的进行周期性数据检查,将前台和后台数据进行比较,所以非常损耗性能. 这是大错而特错的.我甚至在新浪前端面试的时 ...

  9. spring boot(四):thymeleaf使用详解

    在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用.thymeleaf 是新一代的模板引擎,在spring4. ...

  10. SQL Server SQL性能优化之--通过拆分SQL提高执行效率,以及性能高低背后的原因

    复杂SQL拆分优化 拆分SQL是性能优化一种非常有效的方法之一, 具体就是将复杂的SQL按照一定的逻辑逐步分解成简单的SQL,借助临时表,最后执行一个等价的逻辑,已达到高效执行的目的 一直想写一遍通过 ...