原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-the-api/

译者:本屌

备注:仅翻译了使用的一部分,剩下的示例部分篇幅并未翻译,有兴趣的可以自己去看原文,最后有一个示例项目,基本的用法都在里面了

​ 依赖注入框架给我们提供了一个使用少量代码就能将所有东西关联起来的可能。而Dagger 2 就是一个可以生成很多模板代码的DI框架。但是为什么Dagger 2 比其他的DI框架要优秀呢?因为Dagger 2 是当前仅有的一个可以生成完全可跟踪的代码(模仿手写代码),这意味着在依赖图里面不会产生一些不可思议的东西。Dagger 2 和其他DI框架相比,动态机制用的很少(都没有用反射)的情况下生成的代码几乎和手写的性能相差不大。

一、@Inject

​ 首先,对于依赖注入来说最重要的注解是@Inject,标识需要由Dagger注入的依赖对象,在Dagger中有三种形式用@Inject

构造方法注入

构造方法中所有的参数全部由Dagger提供。构造方法上加上这个注解之后,这个类也成为了依赖关系图中的一部分,意味着这个类可以被注入到所需要的地方(限制是不能用@Inject标示多个构造方法)。

public class LoginActivityPresenter {
  
  private LoginActivity loginActivity;
  private UserDataStore userDataStore;
  private UserManager userManager;
  
  @Inject
  public LoginActivityPresenter(LoginActivity loginActivity,
                                UserDataStore userDataStore,
                                UserManager userManager) {
      this.loginActivity = loginActivity;
      this.userDataStore = userDataStore;
      this.userManager = userManager;
  }
}

属性注入

@Inject标示一个属性

public class SplashActivity extends AppCompatActivity {
  
  @Inject
  LoginActivityPresenter presenter;
  @Inject
  AnalyticsManager analyticsManager;
  
  @Override
  protected void onCreate(Bundle bundle) {
      super.onCreate(bundle);
      getAppComponent().inject(this);
  }
}

不过在下面的情况下是手动注入

public class SplashActivity extends AppCompatActivity {
  
  //...
  
  @Override 
  protected void onCreate(Bundle bundle) {
      super.onCreate(bundle);
//Requested depenencies are injected in this moment
      getAppComponent().inject(this);    
  }
}

在上面注入代码被调用之前,所有的依赖对象都是null。

注意:在属性注入中的限制是属性不能被定义为私有的(private),因为DI框架需要对她直接赋值

方法注入

最后一种形式是用@Inject标注一个类中的公开方法(public)

public class LoginActivityPresenter {
  
  private LoginActivity loginActivity;
  
  @Inject 
  public LoginActivityPresenter(LoginActivity loginActivity) {
      this.loginActivity = loginActivity;
  }

  @Inject
  public void enableWatches(Watches watches) {
      //Watches instance required fully constructed LoginActivityPresenter
        watches.register(this);    
  }
}

方法声明的所有参数全部由DI框架提供。为什么需要方法注入这种方式呢?因为当我们想通过对象本身(this 引用)来注入依赖的时候,这种形式就有用了。方法注入会在构造函数被调用之后马上调用,所以这就意味着我们完全跳过了当前类的构造方法,此时当前的类已经被完全创建好了。

二、@Module

  • 标示负责提供(Provides)各种对象(创建)的类,Dagger会知道哪些地方需要哪些对象并且创建它;
@Module
public class GithubApiModule {
  
  @Provides
  @Singleton
  OkHttpClient provideOkHttpClient() {
      OkHttpClient okHttpClient = new OkHttpClient();
      okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS);
      okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS);
      return okHttpClient;
  }

@Provides
@Singleton
RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
      RestAdapter.Builder builder = new RestAdapter.Builder();
      builder.setClient(new OkClient(okHttpClient))
             .setEndpoint(application.getString(R.string.endpoint));
      return builder.build();
  }
}

@Provides

用于Module类中来标记提供依赖对象的方法。

@Module
public class GithubApiModule {
  
  //...
  
  @Provides   //This annotation means that method below provides dependency
  @Singleton
  RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
      RestAdapter.Builder builder = new RestAdapter.Builder();
      builder.setClient(new OkClient(okHttpClient))
             .setEndpoint(application.getString(R.string.endpoint));
      return builder.build();
  }
}

三、@Component

​ 这个注解是用来构建一个 将所有东西结合在一起的接口,在这个注解标示的接口里面,我们定义那些我们需要依赖的Modules或者Components。并且这个接口也是定义哪些依赖公开可见(可被注入)和哪些Component(组件)可以注入对象。

​ 简而言之,@Component注解就像是@Module@Inject之间的桥梁。

@Singleton
@Component(
    modules = {
        AppModule.class,
        GithubApiModule.class
    }
)
public interface AppComponent {
    void inject(GithubClientApplication githubClientApplication);
    Application getApplication();
    AnalyticsManager getAnalyticsManager();
    UserManager getUserManager();
}

@Component也可以依赖于其他的组件,并且也可以限定生命周期(scope)

@ActivityScope
@Component(      
    modules = SplashActivityModule.class,
    dependencies = AppComponent.class
)
public interface SplashActivityComponent {
    SplashActivity inject(SplashActivity splashActivity);
		SplashActivityPresenter presenter();
}

四、@Scope

​ 根据 JSR-330 标准(一个java依赖注入标准),在Dagger2里面@Scope是用来定义一个表示作用域的注解。就像@singletons一样,定义一个依赖在所在的组件中是单例的(并不是整个app的生命周期里面)。

@Scope
public @interface ActivityScope {
}

五、@MapKey

​ 这个注解是用来定义一个Map或者Set的集合,目前只支持两种类型的key - String 和 enum

定义

@MapKey(unwrapValue = true)
@interface TestKey {
  String value();
}

提供

@Provides(type = Type.MAP)
@TestKey("foo")
String provideFooKey() {
  return "foo value";
}

@Provides(type = Type.MAP)
@TestKey("bar")
String provideBarKey() {
  return "bar value";
}

使用

@Inject
Map<String, String> map;

map.toString() // => „{foo=foo value, bar=bar value}”

六、@Qualifier

@Qualifier注解帮我们标记并区分相同类型的依赖。比如你需要两个RestAdapter对象,一个用于Github API,另一个用于Facebook API,@Qualifier可以帮你标记它:

定义名称

@Provides
@Singleton
@GithubRestAdapter  //Qualifier
RestAdapter provideRestAdapter() {
  return new RestAdapter.Builder()
      .setEndpoint("https://api.github.com")
      .build();
}

@Provides
@Singleton
@FacebookRestAdapter  //Qualifier
RestAdapter provideRestAdapter() {
  return new RestAdapter.Builder()
      .setEndpoint("https://api.facebook.com")
      .build();
}

注入依赖

@Inject
@GithubRestAdapter
RestAdapter githubRestAdapter;

@Inject
@FacebookRestAdapter
RestAdapter facebookRestAdapter;

七、示例

https://github.com/zhangzhenxi/SimpleDagger