文/iamwent(简书作者)
原文链接:http://www.jianshu.com/p/7505d92d7748
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

假设你已经了解 依赖注入 这一概念,只是在如何使用 Dagger 时遇到了一些困扰,因为 Dagger 其实是一个上手难度颇高的库。我试图通过这篇文章解决如何上手这一问题。

目前 Dagger 有两个分支,一个由 Square 维护,一个为 Google 在前者的基础上开出的分支,即 Dagger 2 。关于二者的比较,点击此处

本文写作过程中参考了不少优秀的 Dagger 文章,列在文章末尾。

在此一并感谢他们的工作!

Dagger

在引入 Dagger 之前,我们需要了解一些基础概念。Dagger 主要分三块:

  • @Inject:需要注入依赖的地方,Dagger 会构造一个该类的实例并满足它所需要的依赖;
  • @Module:依赖的提供者,Module 类中的方法专门提供依赖,并用 @Provides 注解标记;
  • @Component:依赖的注入者,是 @Inject@Module 的桥梁,它从 @Module 中获取依赖并注入给 @Inject

对于以上关系,一句话解释就是:模块(Module)负责提供依赖,组件(Component)负责注入依赖。

Sourcecode

源码放在 Github: DaggerDemo

Gradle

  1. project/build.gradle

     buildscript {
    ... dependencies {
    ... classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
    }
  2. project/app/build.gradle

     apply plugin: 'com.android.application'
    apply plugin: 'com.neenbedankt.android-apt' android {
    ...
    } dependencies {
    ... //Required by Dagger2
    apt 'com.google.dagger:dagger-compiler:2.0.2'
    compile 'com.google.dagger:dagger:2.0.2'
    // Dagger 2 中会用到 @Generated 注解,而 Android SDK 中没有 javax.anotation.Generated.class,所以在 Android 项目要添加此句
    provided 'org.glassfish:javax.annotation:10.0-b28'
    }

Example

Demo 实现一个简单的 ListView 显示字符串列表

  1. 创建 UserAdapter 类,并在构造函数前添加 @Inject 注解。这样,Dagger 就会在需要获取 UserAdapter 对象时,调用这个被标记的构造函数,从而生成一个 UserAdapter 对象。

     public class UserAdapter extends BaseAdapter {
    private LayoutInflater inflater;
    private List<String> users; @Inject
    public UserAdapter(Context ctx, List<String> users) {
    this.inflater = LayoutInflater.from(ctx);
    this.users = users;
    } ...
    }

    需要注意的是:如果构造函数含有参数,Dagger 会在调用构造对象的时候先去获取这些参数(不然谁来传参?),所以你要保证它的参数也提供可被 Dagger 调用到的生成函数。Dagger 可调用的对象生成方式有两种:一种是用 @Inject 修饰的构造函数,上面就是这种方式。另外一种是用 @Provides 修饰的函数,下面会讲到。参考:Dagger 源码解析

  2. 构建 Module,提供 Context 和 List<String> 依赖,如此, Dagger 生成 UserAdapter 时所需要的依赖就从这里获取。

     @Module
    public class UserModule {
    private static final int COUNT = 10; private final Context context; public UserModule(Context context) {
    this.context = context;
    } @Provides
    @ActivityScope
    Context provideActivityContext() {
    return context;
    } @Provides
    @ActivityScope
    List<String> provideUsers() {
    List<String> users = new ArrayList<>(COUNT); for (int i = 0; i < COUNT; i++) {
    users.add("item " + i);
    } return users;
    }
    }

    @ActivityScope 是一个自定义的范围注解,作用是允许对象被记录在正确的组件中,当然这些对象的生命周期应该遵循 Activity 的生命周期。

     import java.lang.annotation.Retention;
    import static java.lang.annotation.RetentionPolicy.RUNTIME; import javax.inject.Scope; @Scope
    @Retention(RUNTIME)
    public @interface ActivityScope { }
  3. 构建 Component,负责注入依赖

     @ActivityScope
    @Component(modules = {UserModule.class})
    public interface UserComponent {
    void inject(MainActivity activity);
    }

    注意:这里必须是真正消耗依赖的类型 MainActivity,而不可以写成其父类,比如 Activity ,否则会导致注入失败。(参考:使用Dagger 2进行依赖注入)

  4. 完成依赖注入

     public class MainActivity extends AppCompatActivity {
    
         ...
    
         @Inject
    UserAdapter adapter; @Override
    protected void onCreate(Bundle savedInstanceState) { ... // 完成注入
    DaggerUserComponent.builder()
    .userModule(new UserModule(this))
    .build()
    .inject(this); listView.setAdapter(adapter);
    }
    }

    如果找不到 DaggerUserComponent 类,你需要先编译一下整个项目。这是因为 Dagger 是在编译时生成必要的元素,编译时 Dagger 会处理我们的注解,为 @Components 生成实现并命名为 Dagger$${YouComponentClassName},如 UserComponent -> DaggerUserComponent 。你可以在 app/build/generated/source/apt 下找到相关的类。

    实际上,调用 inject 方法最终调用的是以下这样一段代码,更多细节可以查看源码。

     @Override
    public void injectMembers(MainActivity instance) {
    if (instance == null) {
    throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.adapter = adapterProvider.get();
    }

Dagger too

  1. @Inject 和 @Provide 两种依赖生成方式的区别:

    • @Inject 用于注入可实例化的类,@Provides 可用于注入所有类
    • @Inject 可用于修饰属性和构造函数,可用于任何非 Module 类,@Provides 只可用于用于修饰非构造函数,并且该函数必须在某个Module内部
    • @Inject 修饰的函数只能是构造函数,@Provides 修饰的函数必须以 provide 开头
  2. Dagger 的其他注解:

    • @Scope: Dagger 可以通过自定义注解限定注解作用域,参考前面的 @ActivityScope
    • @Qualifier:限定符,当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解来区分。例如:在 Android 中,我们会需要不同类型的 Context,所以我们可以定义 @Qualifier 注解 @ForApplication@ForActivity,这样当注入一个 Context 的时候,我们就可以告诉 Dagger 我们想要哪种类型的 Context。
    • @Singleton:单例模式,依赖的对象只会被初始化一次
  3. Dagger 的实际应用:本例只是一个上手教程,辅助理解 Dagger 的原理及使用方式,具体的项目应用可以参考 Reference 中第 3 条的 Avengers 的源码

Reference

  1. Dagger 2 Official Documentation
  2. Tasting Dagger 2 on Android,中文:详解 Dagger 2
  3. When the Avengers meet Dagger2, RxJava and Retrofit in a clean way,中文:当复仇者联盟遇上 Dragger2、RxJava 和 Retrofit 的巧妙结合
  4. 使用 Dagger 2 进行依赖注入
  5. Dagger 源码解析 (PS: 这是 Dagger 1,但是很有参考价值)

Dagger 2: Step To Step的更多相关文章

  1. Step by step Dynamics CRM 2011升级到Dynamics CRM 2013

    原创地址:http://www.cnblogs.com/jfzhu/p/4018153.html 转载请注明出处 (一)检查Customizations 从2011升级到2013有一些legacy f ...

  2. Step by Step 创建一个新的Dynamics CRM Organization

    原创地址:http://www.cnblogs.com/jfzhu/p/4012833.html 转载请注明出处 前面演示过如何安装Dynamics CRM 2013,参见<Step by st ...

  3. Step by step Install a Local Report Server and Remote Report Server Database

    原创地址:http://www.cnblogs.com/jfzhu/p/4012097.html 转载请注明出处 前面的文章<Step by step SQL Server 2012的安装 &g ...

  4. Step by step Dynamics CRM 2013安装

    原创地址:http://www.cnblogs.com/jfzhu/p/4008391.html 转载请注明出处   SQL Server可以与CRM装在同一台计算机上,也可安装在不同的计算机上.演示 ...

  5. Step by step 活动目录中添加一个子域

    原创地址:http://www.cnblogs.com/jfzhu/p/4006545.html 转载请注明出处 前面介绍过如何创建一个域,下面再介绍一下如何在该父域中添加一个子域. 活动目录中的森林 ...

  6. SQL Server 维护计划实现数据库备份(Step by Step)(转)

    SQL Server 维护计划实现数据库备份(Step by Step) 一.前言 SQL Server 备份和还原全攻略,里面包括了通过SSMS操作还原各种备份文件的图形指导,SQL Server  ...

  7. 转:eclipse以及step into step over step return的区别

    首先来讲一下step into step over step return的区别: step into就是单步执行,遇到子函数就进入并且继续单步执行:(F5) step over是在单步执行时,在函数 ...

  8. [转]Bootstrap 3.0.0 with ASP.NET Web Forms – Step by Step – Without NuGet Package

    本文转自:http://www.mytecbits.com/microsoft/dot-net/bootstrap-3-0-0-with-asp-net-web-forms In my earlier ...

  9. EF框架step by step(7)—Code First DataAnnotations(2)

    上一篇EF框架step by step(7)—Code First DataAnnotations(1)描述了实体内部的采用数据特性描述与表的关系.这一篇将用DataAnnotations描述一下实体 ...

  10. EF框架step by step(6)—处理实体complex属性

    上一篇的中介绍过了对于EF4.1框架中,实体的简单属性的处理 这一篇介绍一下Code First方法中,实体Complex属性的处理.Complex属性是将一个对象做为另一个对象的属性.映射到数据库中 ...

随机推荐

  1. Sqlserver基于流程控制

    流程控制语句只能在单个批处理段,用户自定义函数和存储过程中使用不能夸多个批处理或者用户自定义函数或者存储过程 批处理:一个或者多个语句组成的一个批处理,是因为所有语句一次性地被提交到一个sql实例,如 ...

  2. 公共语言运行库(CLR)和中间语言(IL)(一)

    公共语言运行库(.net运行库)即CLR 1.C#先编译为IL,IL为ms的中间语言,IL是平台无关性的. 2.CLR再将IL编译为平台专用语言. 3.CLR在编译IL时为即时编译(JIT) VB.V ...

  3. javascript 第27节 jQuery选择器

    下面的html需要以下2个文件: 1.style.css div,span,p { width:140px; height:140px; margin:5px; background:#aaa; bo ...

  4. java中进制之间的转换

    //十进制转其他进制 Integer.toHexString(10); //将10转换为十六进制,返回字符串类型 Integer.toOctalString(10); //将10转为八进制,返回字符串 ...

  5. 模板:LCS(最长公共子序列)

    #include <cstring> #define max(a,b) ((a) > (b) ? (a) : (b)) int same(char ch1,char ch2) { ; ...

  6. C++对象创建与释放

    创建对象有以下四种形式: #include <iostream> using namespace std; class A{ private: int i; public: A(){ co ...

  7. 请求与通配符 mime 映射相匹配。请求映射到静态文件处理程序。如果有不同的前提条件,请求将映射到另一个处理程序。

    打开IIS管理器,找到“处理程序映射”,在列表右击选择“添加脚本映射”即可.eg:*.aspx,将该类型的页面的处理程序映射为“%windir%\Microsoft.NET\Framework\v4. ...

  8. Hash算法初见

    hash算法 (hashmap 实现原理)   Hash ,一般翻译做“ 散列” ,也有直接音译为“ 哈希” 的,就是把任意长度的输入(又叫做预映射, pre-image ),通过散列算法,变换成固定 ...

  9. PHP 向 MySql 中数据修改操作时,只对数字操作有效,非数字操作无效,怎么办?

    问题描述:   用PHP向MySql数据库中修改数据,实现增删改(数据库能正确连接) 经测试,代码只能对数字进行正常的增删改操作,非数字操作无效   但要在课程名称中输入中文,应该如果修改呢?   存 ...

  10. javascript 通用loading动画效果

    由于项目中多处要给ajax提交的时候增加等待动画效果,所以就写了一个简单的通用js方法: 代码如下: /*ajax提交的延时等待效果*/ var AjaxLoding = new Object(); ...