使用Dagger 2依赖注入 - API
这章是展示使用Dagger 2在Android端实现依赖注入的系列中的一部分。今天我会探索Dagger 2的基础并且学习这个依赖注入框架的所有的API。 Dagger 2在我上一章中我提到了DI框架给我们带来了什么 - 它让只写一小部分代码就可以使一切联系在一起成为可能。Dagger 2就是DI框架的一个例子,它可以为我们生成很多模版代码。但是为什么它比其它的要更好呢?目前为止它是唯一一个可以生成完整模仿用户手写的可追踪的代码的DI框架。这意味着让依赖图表构建过程不再充满魔幻。Dagger 2相比其它是不具有动态性的(使用时完全不使用反射)但是生成的代码的简洁性和性能都是与手写的代码同水准的。 Dagger 2 基础下面是Dagger 2的API: public @interface Component {
Class<?>[] modules() default {};
Class<?>[] dependencies() default {};
}
@interface Subcomponent {
Class<?>[] modules() @interface Module {
Class<?>[] includes() @interface Provides {
}
@interface MapKey {
boolean unwrapValue() default true;
}
public interface Lazy<T> {
T get();
}
还有在Dagger 2中用到的定义在JSR-330(Java中依赖注入的标准)中的其它元素: @interface Inject {
}
@interface Scope {
}
@interface Qualifier {
}
让我们来学习它们。 @Inject 注解 DI中第一个并且是最重要的就是 构造器注入: class LoginActivityPresenter {
private LoginActivity loginActivity;
private UserDataStore userDataStore;
private UserManager userManager;
public LoginActivityPresenter(LoginActivity loginActivity,UserDataStore userDataStore,UserManager userManager) {
this.loginActivity = loginActivity;
this.userDataStore = userDataStore;
this.userManager = userManager;
}
}
所有的参数都是通过依赖图表中获取。 LoginActivity extends BaseActivity {
LoginActivityPresenter presenter;
//...
}
这个例子中的局限性是我们不能给这个类中的多个构造器作 属性注入 另一种选择是给指定的属性作 SplashActivity AppCompatActivity {
LoginActivityPresenter presenter;
AnalyticsManager analyticsManager;
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
getAppComponent().inject(this);
}
}
但是在这个例子中,注入过程必须要在我们类的某处“手动”调用: AppCompatActivity {
//...
this); //Requested depenencies are injected in this moment
}
}
在这个调用之前,我们的依赖是null值。 属性注入的局限性是我们不能使用 //This class is generated automatically by Dagger 2
public final SplashActivity_MembersInjector implements MembersInjector<SplashActivity> {
//...
public 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();
}
}
方法注入 最后一种方法使用 private LoginActivity loginActivity;
LoginActivityPresenter(LoginActivity loginActivity) {
this.loginActivity = loginActivity;
}
(Watches watches) {
watches.register(this); //Watches instance required fully constructed LoginActivityPresenter
}
}
方法的所有参数都是通过依赖图表提供的。但是为什么我们需要方法注入呢?在某些情况下会用到,如当我们希望传入类的当前实例( @Module 注解@Module是Dagger 2 API的一部分。这个注解用于标记提供依赖的类 - 多亏它Dagger才会知道某些地方需要的对象被构建。 @Module
GithubApiModule {
@Provides
@Singleton
OkHttpClient provideOkHttpClient() {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(60 * 1000,TimeUnit.MILLISECONDS);
okHttpClient.setReadTimeout(60 * 1000,TimeUnit.MILLISECONDS);
return okHttpClient;
}
@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();
}
}
view rawGithubApiModule.java hosted with ? by GitHub
@Provides 注解 这个注解用在 GithubApiModule {
//...
//This annotation means that method below provides dependency
builder.build();
}
}
@Component 注解 这个注解用在把一切联系在一起的接口上面。在这里我们可以定义从哪些module(或者哪些Components)中获取依赖。这里也可以用来定义哪些图表依赖应该公开可见(可以被注入)和哪里我们的component可以注入对象。 这里有个使用了两个module的 @Singleton
(
modules = {
AppModule.class,GithubApiModule.class
}
)
AppComponent {
inject(GithubClientApplication githubClientApplication);
Application getApplication();
AnalyticsManager getAnalyticsManager();
UserManager getUserManager();
}
@Component也可以依赖其它的component,并且定义了生命周期(我会在以后的文章中讲解Scope): @ActivityScope
(
modules = SplashActivityModule.class,dependencies = AppComponent.class
)
SplashActivityComponent {
SplashActivity inject(SplashActivity splashActivity);
SplashActivityPresenter presenter();
}
@Scope 注解@Scope
@interface ActivityScope {
}
JSR-330标准的又一部分。在Dagger 2中, 接下来是一些不太重要,不会经常用到的东西: MapKey这个注解用在定义一些依赖集合(目前为止,Maps和Sets)。让例子代码自己来解释吧: 定义: @MapKey(unwrapValue = true)
TestKey {
String value();
}
提供依赖: @Provides(type = Type.MAP)
("foo")
String provideFooKey() {
return "foo value";
}
)
String provideBarKey() {
"bar value";
}
使用: @Inject
Map<String,String> map;
map.toString() // => ?{foo=foo value,bar=bar value}”
@MapKey注解目前只提供两种类型 - String和Enum。 @Qualifier@Qualifier注解帮助我们去为相同接口的依赖创建“tags”。想象下你需要提供两个 命名依赖: @Provides
//Qualifier
RestAdapter provideRestAdapter() {
return new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.build();
}
//Qualifier
RestAdapter "https://api.facebook.com")
.build();
}
注入依赖: @Inject
RestAdapter githubRestAdapter;
RestAdapter facebookRestAdapter;
以上就是全部了。我们刚刚已经了解了所有Dagger 2 API中重要的元素了。 App example现在是时候让我们在实践中检验我们的知识了。我们会基于Dagger 2来实现一个简单的Github客户端app。 想法我们的Github客户端有三个Activity和非常简单的使用case。它的全部流程:
这里就是我们的app看起来的样子:
在内部,从DI角度我们的app结构看起来如下: 简单说 - 每一个Activity都有它自己的依赖图表。每个图表(_Component类)有两个对象 - 讲讲 GithubApiModule提供了一些依赖,比如 文档中的例子: 提供依赖的方法: SomeType getSomeType();
Set<SomeType> getSomeTypes();
@PortNumber int getPortNumber();
此外,我们必须要定义哪里我们希望要去注入依赖(通过成员注入)。在我们例子中 成员注入的方法: getSomeType();
Provider<SomeType> getSomeTypeProvider();
Lazy<SomeType> getLazySomeType();
实现我不想深入太多的代码。cloneGithubClient代码并导入到最新的Android Studio。这里给出一些开始的提示: Dagger 2 安装 只需要获得 AppComponent 从 AppComponent的实现由Dagger 2(对象可以通过builder模式被创建)生成的代码提供。在这里我们也可以放入所有的Component的依赖(module和其他components)。 限定的component 可以通过 在这里我们也会提供AppComponent实例(因为 剩下的就靠你自己了。认真的说 - 尝试弄清楚一切是怎么合适地联系在一起的。下一章节我们尝试深入Dagger 2的那些紧密的元素(在底层它们是怎么工作的)。 代码:以上描述的完整代码可见Githubrepository。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |