zoukankan      html  css  js  c++  java
  • Dagger 2: Step To Step

    文/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,但是很有参考价值)
  • 相关阅读:
    hdu 4710 Balls Rearrangement()
    hdu 4707 Pet(DFS水过)
    hdu 4706 Children's Day(模拟)
    hdu 4712 Hamming Distance(随机函数暴力)
    csu 1305 Substring (后缀数组)
    csu 1306 Manor(优先队列)
    csu 1312 榜单(模拟题)
    csu 1303 Decimal (数论题)
    网络爬虫
    Python处理微信利器——itchat
  • 原文地址:https://www.cnblogs.com/krislight1105/p/5263687.html
Copyright © 2011-2022 走看看