[Android]使用Dagger 2依赖注入 - API(翻译)


以下内容为原创,欢迎转载,转载请注明
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092525.html

使用Dagger 2依赖注入 - API

原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-the-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 {};
}

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();
}

还有在Dagger 2中用到的定义在 JSR-330 (Java中依赖注入的标准)中的其它元素:

public @interface Inject {
}

public @interface Scope {
}

public @interface Qualifier {
}

让我们来学习它们。

@Inject 注解

DI中第一个并且是最重要的就是@Inject注解。JSR-330标准中的一部分,标记那些应该被依赖注入框架提供的依赖。在Dagger 2中有3种不同的方式来提供依赖:

构造器注入:

@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 LoginActivity extends BaseActivity {

    @Inject
    LoginActivityPresenter presenter;

    //...
}

这个例子中的局限性是我们不能给这个类中的多个构造器作@Inject注解。

属性注入

另一种选择是给指定的属性作@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);
        getAppComponent().inject(this);    //Requested depenencies are injected in this moment
    }
}

在这个调用之前,我们的依赖是null值。

属性注入的局限性是我们不能使用private。为什么?简单说,生成的代码会直接地调用它们来设置属性,就像这里:

//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方法中作注解:

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 注解

@Module是Dagger 2 API的一部分。这个注解用于标记提供依赖的类 - 多亏它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();
    }
}
view rawGithubApiModule.java hosted with  by GitHub

@Provides 注解

这个注解用在@Module类中。@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 注解

这个注解用在把一切联系在一起的接口上面。在这里我们可以定义从哪些module(或者哪些Components)中获取依赖。这里也可以用来定义哪些图表依赖应该公开可见(可以被注入)和哪里我们的component可以注入对象。@Component通常是@Module@Inject之间的桥梁。

这里有个使用了两个module的@Component例子代码,可以注入依赖到GithubClientApplication,并且标记了三个依赖公开可见:

@Singleton
@Component(
    modules = {
        AppModule.class,
        GithubApiModule.class
    }
)
public interface AppComponent {

    void inject(GithubClientApplication githubClientApplication);

    Application getApplication();

    AnalyticsManager getAnalyticsManager();

    UserManager getUserManager();
}

@Component也可以依赖其它的component,并且定义了生命周期(我会在以后的文章中讲解Scope):

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

    SplashActivityPresenter presenter();
}

@Scope 注解

@Scope
public @interface ActivityScope {
}

JSR-330标准的又一部分。在Dagger 2中,@Scope被用于标记自定义的scope注解。简单说它们可以类似单例地标记依赖。被作注解的依赖会变成单例,但是这会与component的生命周期(不是整个应用)关联。但是正如我刚才所说 - 我会在下一篇文章中深入scope。现在值得一提的是所有自定义scope做的是同样的事情(从代码角度)- 它们为对象保持单例。但是他们也会被在图表认证处理中使用,这对尽可能地捕捉图表结构问题是有帮助的。



接下来是一些不太重要,不会经常用到的东西:

MapKey

这个注解用在定义一些依赖集合(目前为止,Maps和Sets)。让例子代码自己来解释吧:

定义:

@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}”

@MapKey注解目前只提供两种类型 - String和Enum。

@Qualifier

@Qualifier注解帮助我们去为相同接口的依赖创建“tags”。想象下你需要提供两个RestAdapter对象 - 一个用于Github API,另一个用于Fackbook 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;

以上就是全部了。我们刚刚已经了解了所有Dagger 2 API中重要的元素了。

App example

现在是时候让我们在实践中检验我们的知识了。我们会基于Dagger 2来实现一个简单的Github客户端app。

想法

我们的Github客户端有三个Activity和非常简单的使用case。它的全部流程:

  1. 输入Github用户名
  2. 如果用户存在则显示它所有公开的库。
  3. 当用户按下list的item后显示库的详情。

这里就是我们的app看起来的样子:

在内部,从DI角度我们的app结构看起来如下:

简单说 - 每一个Activity都有它自己的依赖图表。每个图表(_Component类)有两个对象 - _Prresenter_Activity。每个component也会从全局的component(AppComponent,包含了Application, UserManager, RepositoriesManager等)中获取依赖。

讲讲AppComponent - 看上面最近的图表。它包含了两个modules:AppModuleGithubApiModule

GithubApiModule提供了一些依赖,比如OkHttpClientRestAdapter,他们只会在这个module的其它依赖使用。在Dagger 2中我们可以控制哪些对象对外部的component可见。在我们例子中我们不希望暴露这些被使用了的对象。所以我们只是暴露了UserManagerRepositoriesManager,因为这有这些对象才会被我们的Activity用到。所有都通过public、返回非void类型并无参数的方法被定义。

文档中的例子:

提供依赖的方法

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

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

文档中的例子:

成员注入的方法

SomeType getSomeType();
Provider<SomeType> getSomeTypeProvider();
Lazy<SomeType> getLazySomeType();

实现

我不想深入太多的代码。clone GithubClient 代码并导入到最新的Android Studio。这里给出一些开始的提示:

Dagger 2 安装

只需要获得/app/build.gradle文件。我们需要增加Dagger 2依赖和使用 android-apt 插件来对生成的代码和Android Studio IDE之间进行绑定。

AppComponent

GithubClientApplication类开始探索。在这里AppComponent会被创建和存储。这意味着所有单例的对象的生命周期都会与Applicaiton一致(所以每时每刻都是如此)。

AppComponent的实现由Dagger 2(对象可以通过builder模式被创建)生成的代码提供。在这里我们也可以放入所有的Component的依赖(module和其他components)。

限定的component

可以通过SplashActivity来探索Activities Components是怎么创建的。它重写了setupActivityComponent(AppComponent),在这里它创建它自己的Component(SplashActivityComponent),然后注入所有被做@Inject注解的依赖(这个例子中的SplashActivityPresenterAnalyticsManager)。

在这里我们也会提供AppComponent实例(因为SplashActivityComponent是依赖它的)以及SplashActivityModule(它提供了Presenter和Activity实例)。

剩下的就靠你自己了。认真的说 - 尝试弄清楚一切是怎么合适地联系在一起的。下一章节我们尝试深入Dagger 2的那些紧密的元素(在底层它们是怎么工作的)。

代码:

以上描述的完整代码可见Github repository

作者

Miroslaw Stanek

Head of Mobile Development @ Azimo

[Android]使用Dagger 2依赖注入 - DI介绍(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5092083.html

[Android]使用Dagger 2依赖注入 - API(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5092525.html

[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5095426.html

[Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5098943.html

[Android]Dagger2Metrics - 测量DI图表初始化的性能(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5193437.html

[Android]使用Dagger 2进行依赖注入 - Producers(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6234811.html

[Android]在Dagger 2中使用RxJava来进行异步注入(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6236646.html

[Android]使用Dagger 2来构建UserScope(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6237731.html

[Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6266442.html

时间: 2024-10-06 08:10:17

[Android]使用Dagger 2依赖注入 - API(翻译)的相关文章

[Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5098943.html 使用Dagger 2依赖注入 - 图表创建的性能 原文:http://frogermcs.github.io/dagger-graph-creation-performance/ #PerfMatters - 最近非常流行标签,尤其在Android世界中.不管怎样,apps只需要正常工作就可以的时代已经过去了.现在所有的一切都应该是令人愉

[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5095426.html 使用Dagger 2依赖注入 - 自定义Scope 原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/ 这章是展示使用Dagger 2在Android端实现依赖注入的系列中的一部分.今天我会花点时间在自定义Scope(作用域)上面

[Android]使用Dagger 2依赖注入 - DI介绍(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092083.html 使用Dagger 2依赖注入 - DI介绍 原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-introdution-to-di/ 不久之前,在克拉科夫的 Tech Space 的 Google I/O 扩展中,我 展示 了一些关于使用Dagger 2来进行依

Android 使用dagger2进行依赖注入(基础篇)

0. 前言 Dagger2是首个使用生成代码实现完整依赖注入的框架,极大减少了使用者的编码负担,本文主要介绍如何使用dagger2进行依赖注入.如果你不还不了解依赖注入,请看这一篇. 1. 简单的依赖注入 首先我们构建一个简单Android应用.我们创建一个UserModel,然后将它显示到TextView中.这里的问题是,在创建UserModel的时候,我们使用了前文所说的hard init.一旦我们的UserModel的创建方式发生了改变(比如需要传入Context对象到构造函数),我们就需

[Android]使用Dagger 2进行依赖注入 - Producers(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6234811.html 使用Dagger 2进行依赖注入 - Producers 原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-producers/ 本文是在Android中使用Dagger 2框架进行依赖注入的系列文章中的一部分.今天我们将探索下Dagger Producers

[Android]在Dagger 2中使用RxJava来进行异步注入(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6236646.html 在Dagger 2中使用RxJava来进行异步注入 原文:http://frogermcs.github.io/async-injection-in-dagger-2-with-rxjava 几星期前我写了一篇关于在Dagger 2中使用Producers进行异步注入的文章.在后台线程中执行对象的初始化又一个很好的优势 - 它负责实时(每

[Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6266442.html 在Dagger 2中Activities和Subcomponents的多绑定 原文:http://frogermcs.github.io/activities-multibinding-in-dagger-2 几个月前,在MCE^3会议中,Gregory Kick在他的演讲中展示了一个提供Subcomponents(比如,为Activit

[Android]使用Dagger 2来构建UserScope(翻译)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6237731.html 使用Dagger 2来构建UserScope 原文:http://frogermcs.github.io/building-userscope-with-dagger2/ 在Dagger 2中自定义scopes可以在不寻常存活时间(与Application和界面生命周期不同的)的依赖上给我带来更好的控制.但是在Android app中正确

Dagger——Android 的依赖注入框架

简介 在开发程序的时候,会用到各种对象,很多对象在使用之前都需要进行初始化.例如你要操作一个SharedPreference,你需要调用getSharedPreferences(String name,int mode)来获取一个对象,然后才能使用它.而如果这个对象会在多个Activity中被使用,你就需要在每个使用的场景中都写下同样的代码.这不仅麻烦,而且增加了出错的可能.dagger的用途就是:让你不需要初始化对象.换句话说,任何对象声明完了就能直接用. 原理 dagger是使用依赖注入的方