Dagger 2 demo, Step To Step
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app
gradle/wrapper
.gitignore
README.md
build.gradle
gradle.properties
gradlew
gradlew.bat
settings.gradle

README.md

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

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

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

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

Dagger

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

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

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

Sourcecode

Gradle

app/build.gradle

```groovy

dependencies {
    ...

   // Add Dagger dependencies,2017.04.26
  compile 'com.google.dagger:dagger:2.11-rc1'
  annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc1'
}
```

Example

Demo 实现一个简单的 ListView 显示字符串列表,源码在 Github : DaggerDemo

  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 依赖,如此, 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,但是很有参考价值)