Dagger2-Dagger API

介绍Dagger2的一些基础API

Dagger2基础

下面是Dagger2的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public @interface Component {
Class<?>[] modules() default {};
Class<?>[] dependencies() default {};
}
public @interface Subcomponent {
Class<?>[] modules() default {};
}
public @interface Module {
Class<?>[] includes() default {};
}
public @interface Provides {
}
public @interface MapKey {
boolean unwrapValue() default true;
}
public interface Lazy<T> {
T get();
}

Dagger2中还有一些其他元素通过Java注解规范来定义:

1
2
3
4
5
6
7
8
public @interface Inject {
}
public @interface Scope {
}
public @interface Qualifier {
}

@Inject annotation

Dagger中最重要也是第一个注解@Inject,标记那些需要被DI框架所提供的依赖对象。在Dagger2中有3中不同的方式去提供依赖:

构造器注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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注解被使用在这个类的构造器使得这个构造器也被列入到依赖图的一部分。也就是当他需要的时候会被注入需要的依赖。

1
2
3
4
5
6
7
public class LoginActivity extends BaseActivity {
@Inject
LoginActivityPresenter presenter;
//...
}

这种情况下的限制是,同一个类只能在一个构造函数中使用@Inject

属性注入

另外一个选项是通过注解@Inject在定义的属性中:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SplashActivity extends AppCompatActivity {
@Inject
LoginActivityPresenter presenter;
@Inject
AnalyticsManager analyticsManager;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
getAppComponent().inject(this);
}
}

在这种情况下,注入过程需要我们在某个位置手动调用:

1
2
3
4
5
6
7
8
9
10
public class SplashActivity extends AppCompatActivity {
//...
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
getAppComponent().inject(this); //Requested depenencies are injected in this moment
}
}

在未被调用前,他们的依赖是null值。

属性注入的缺陷是,我们不能用private来标注他们。简单说,生成的代码会直接地调用它们来设置属性,就像这里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//This class is generated automatically by Dagger 2
public final class SplashActivity_MembersInjector implements MembersInjector<SplashActivity> {
//...
@Override
public void injectMembers(SplashActivity splashActivity) {
if (splashActivity == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
supertypeInjector.injectMembers(splashActivity);
splashActivity.presenter = presenterProvider.get();
splashActivity.analyticsManager = analyticsManagerProvider.get();
}
}

方法注入

最后一种提供依赖的方法是注解@Inject在这个类的public方法上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class LoginActivityPresenter {
private LoginActivity loginActivity;
@Inject
public LoginActivityPresenter(LoginActivity loginActivity) {
this.loginActivity = loginActivity;
}
@Inject
public void enableWatches(Watches watches) {
watches.register(this); //Watches instance required fully constructed LoginActivityPresenter
}
}

方法中的参数都是通过依赖图提供。为什么我们需要方法注入?在某些情况下,我们希望传入类的当前实例(this引用)到注入的依赖中。方法注入会在构造器调用后马上被调用,所以我们传入的this是完全已经被构造完成的。

@Module annotation

这个注解用来标识这个类是用来提供依赖的。Dagger通过它来知道那些地方需要被构建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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();
}
}

@Provider annotation

这个注解用在@Module类中。标注那些在Module中返回的依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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 annotation

这个注解用来构建接口把所有联系在一起。在这个地方我们定义我们依赖了那些Module或者其他Component。我们也在这里定义了那些依赖图应该公开可见(可以被注入的),和哪里的Component可以注入对象。@Component@Module@Inject的桥梁。

例子中的@Component使用了两个Modules,可以注入依赖给GithubClientApplication并且对外暴露了3个可见的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Singleton
@Component(
modules = {
AppModule.class,
GithubApiModule.class
}
)
public interface AppComponent {
//提供给GithubClientApplication
void inject(GithubClientApplication githubClientApplication);
//暴露三个可见的依赖
Application getApplication();
AnalyticsManager getAnalyticsManager();
UserManager getUserManager();
}

并且@Component可以依赖于其他的Component,而且定义了生命周期。(下面的文章会提到)

1
2
3
4
5
6
7
8
9
10
@ActivityScope
@Component(
modules = SplashActivityModule.class,
dependencies = AppComponent.class
)
public interface SplashActivityComponent {
SplashActivity inject(SplashActivity splashActivity);
SplashActivityPresenter presenter();
}

@Scope annotation

在Dagger2中,@Scope被用于标记自定义注解。简单说他们使得依赖有点跟单例相同。注解的依赖变成单例,但是生命周期会与Component关联(不是整个应用的生命周期)。

@Qulifiter annotation

@Qulifiter注解帮助我们去为相同的接口依赖创建“Tags”。假如需要提供两个RestAdapter对象,一个用于GithubApi,一个用于FaceBookApi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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();
}

注入依赖:

1
2
3
4
5
6
7
@Inject
@GithubRestAdapter
RestAdapter githubRestAdapter;
@Inject
@FacebookRestAdapter
RestAdapter facebookRestAdapter;

App Example

想法:
我们的Github客户端有三个Activity,使用案例非常简单。
1.输入Github的用户名。
2.如果用户存在,则展示所有的公开代码仓库
3.节当用户点击一个仓库展示代码仓库细

看起来是这个样子:

在内部的实现,我们用DI角度构建的App结构看起来是:

概括来说,每一个Activity都有一个属于自己的依赖图。每个依赖图(_Component类)都拥有两个对象-_Presenter_Activity。并且,每个组件,都依赖了全局的Global 组件-AppComponent, AppComponent组件包括了Application,UserManager,RepositoriesManager等。

讲讲AppComponent,认真观察可以发现这个接口包含了两个Module:AppModuleGithubApiModule
GithubApiModule提供了一些依赖:OkhttpClientRestAdapter,他们只会在这个Module的其他依赖中注入。Dagger2可以控制哪些依赖对外部的组件可见。在我们的例子我们不希望暴露上面的依赖给外部。相对的,我们暴露了UserManagerRepositoriesManager。因为只有这些类在Activitys中才被使用。所有的这些都通过public方法来定义,不用传入参数,并且会返回非空的类型。
文档中的例子:
提供依赖的方法:

1
2
3
SomeType getSomeType();
Set<SomeType> getSomeTypes();
@PortNumber int getPortNumber();

此外,我们必须定义哪里我们希望去注入依赖(通过成员注入)。在我们的例子中AppComponent没有任何地方可以去注入。因为它是作为我们Scope组件的依赖。并且他们每个都要定义一个inject(_Activity activity)方法。在这里我们也有一些简单的规则-通过注入单个参数的方法被定义(定义一个实例,它代表我们需要往这个实例中注入依赖),他可以有任意的名字, 但是必须要返回空或者被传入的参数的类型。

文档中的例子:
成员注入的方法:

1
2
3
SomeType getSomeType();
Provider<SomeType> getSomeTypeProvider();
Lazy<SomeType> getLazySomeType();