[译] 全新 Android 注入器:Dagger 2(一)

Dagger 2.10 新增了 Android Support 和 Android Compiler 两大模块。对咱们来讲,本次改动很是之大,全部 Android 开发者都应尽早尝试使用这个新的 Android 依赖注入框架。html

在我开始介绍新的 AndroidInjector 类以及 Dagger 2.11 库以前,若是你对 Dagger 2 还不熟悉甚至以前根本没用过,那我强烈建议你先去看看 Dagger 入门指南,弄清楚什么是依赖注入。为何这么说呢?由于 Android Dagger 涉及到大量注解,学起来会比较吃力。在我看来,学 Android Dagger 以前你最好先去学学 Dagger 2 和依赖注入。这里有一篇关于依赖注入的入门文章 Blog 1 以及一篇关于 Dagger 2 的文章 Blog 2前端

老用法

Dagger 2.10 以前,Dagger 2 是这样用的:java

((MyApplication) getApplication())        
.getAppComponent()        
.myActivity(new MyActivityModule(userId))       
.build()        
.inject(this);
复制代码

这会有什么问题呢?咱们想用依赖注入,可是依赖注入的核心原则是什么?android

一个类不该该关心它是如何被注入的ios

所以咱们必须把这些 Builder 方法和 Module 实例建立部分去掉。git

示例工程

我建立的示例工程中没作什么,我想让它尽量地简单。它里面仅包含 MainActivityDetailActivity 两个 Activity,它们都注入到了相应的 Presenter 实现类而且请求了网络接口(并非真的发起了 HTTP 请求,我只是写了一个假方法)。github

准备工做

在 build.gradle 中加入如下依赖:数据库

compile 'com.google.dagger:dagger:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
compile 'com.google.dagger:dagger-android-support:2.11-rc2'
复制代码

工程包结构

Application 类利用 AppComponent 构建了一张图谱。AppComponent 类的头部都被加上 @Component 注解,当 AppComponent 利用它的 Module 进行构建的时候,咱们将获得一张拥有全部所需实例对象的图谱。举个例子,当 App Module 提供了ApiService,咱们在构建拥有 App Module 的 Component 时将会获得 ApiService 实例对象。后端

若是咱们想将 Activity 加入到 Dagger 图谱中从而可以直接从父 Compponent 直接获取所需实例,咱们只需简单地将 Activity 加上 @Subcomponent 注解便可。在咱们的示例中,DetailActivityComponentMainActivityComponent 类都被加上了 @Subcomponent 注解。最后咱们还有一个必需步骤,咱们须要告诉父 Component 相关的子 Component 信息,所以全部的根 Compponent 都能知道它全部的子 Component。api

先别着急,我后面会解释 @Subcomponent@Component 以及 DispatchActivity 都是什么的。如今只是想让你对 @Component@Subcomponent 有一个大概了解。

@Component and @Component.Builder

**@Component**(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        ActivityBuilder.class})
public interface AppComponent {

    **@Component.Builder**
    interface Builder {
        **@BindsInstance** _Builder application(Application application);_
        _AppComponent build();_
    }

    void inject(AndroidSampleApp app);
}
复制代码

**@Component:**Component 是一个图谱。当咱们构建一个 Component时,Component 将利用 Module 提供被注入的实例对象。

**@Component.Builder:**咱们可能须要绑定一些实例对象到 Component 中,这种状况咱们能够经过建立一个带 @Component.Builder 注解的接口,而后就能够向 builder 中任意添加咱们想要的方法。在个人示例中,我想将 Application 加入到 AppComponent中。

注意:若是你想为你的 Component 建立一个 Builder,那你的 Builder 接口中须要有一个返回类型为你所建立的 Component 的 builder() 方法。

注入 AppComponent

DaggerAppComponent
        ._builder_()
        **.application(this)**
        .build()
        .inject(this);
复制代码

从上面的代码能够看出,咱们将 Application 实例绑定到了 Dagger 图谱中。

我想你们已经对 @Component.Builder@Component 有了必定的认识,下面我想说说工程的结构。

Component/Module 结构

使用 Dagger 的时候咱们能够将 App 分为三层:

  • Application Component
  • Activity Components
  • Fragment Components

Application Component

@Component(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        ActivityBuilder.class})
public interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance Builder application(Application application);
        AppComponent build();
    }

    void inject(AndroidSampleApp app);
}
复制代码

每一个 Android 应用都有一个 Application 类,这就是为何我也有一个 Application Component 的缘由。这个 Component 表示是为应用层面提供实例的 (例如 OkHttp, Database, SharedPrefs)。这个 Component 是 Dagger 图谱的根,在咱们的应用中 Application Component 提供了三个 Module

  • AndroidInjectionModule:这个类不是咱们写的,它是 Dagger 2.10 中的一个内部类,经过给定的 Module 为咱们提供了 Activity 和 Fragment。
  • ActivityBuilder:咱们本身建立的 Module,这个 Module 是给 Dagger 用的,咱们将全部的 Activity 映射都放在了这里。Dagger 在编译期间能获取到全部的 Activity,咱们的 App 中有 MainActivity 和 DetailActivity 两个 Activity,所以我将这两个 Activity 都放在这里。
@Module
public abstract class ActivityBuilder {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainActivityComponent.Builder builder);

    @Binds
    @IntoMap
    @ActivityKey(DetailActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindDetailActivity(DetailActivityComponent.Builder builder);

}
复制代码
  • AppModule:咱们在这里提供了 retrofit、okhttp、持久化数据库、SharedPrefs。其中有一个很重要的细节,咱们必须将子 Component 加入到 AppModule 中,这样 Dagger 图谱才能识别。
@Module(subcomponents = {
        MainActivityComponent.class,
        DetailActivityComponent.class})
public class AppModule {

    @Provides
    @Singleton
    Context provideContext(Application application) {
        return application;
    }

}
复制代码

Activity Components

咱们有两个 Activity:MainActivity and DetailActivity。它们都拥有本身的 ModuleComponent,可是它们与我在上面 AppModule 中定义的同样,也是子 Component

  • MainActivityComponent:这个 Component 是链接 MainActivityModule 的桥梁,可是有一个很关键的不一样点就是不须要在 Component 中添加 inject() 和 build() 方法。MainActivityComponent 会从父类中集成这些方法。AndroidInjector 类是 dagger-android 框架中新增的。
@Subcomponent(modules = MainActivityModule.class)
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity>{}
}
复制代码
  • MainActivityModule:这个 ModuleMainActivity 提供了相关实例对象(例如 MainActivityPresenter)。你注意到 provideMainView() 方法将 MainActivity 做为参数了吗?没错,咱们利用 MainActivityComponent 建立了咱们所需的对象。所以 Dagger 将咱们的 Activity 加入到 图谱中并所以能使用它。
@Module
public class MainActivityModule {

    @Provides
    MainView provideMainView(MainActivity mainActivity){
        return mainActivity;
    }

    @Provides
    MainPresenter provideMainPresenter(MainView mainView, ApiService apiService){
        return new MainPresenterImpl(mainView, apiService);
    }
}
复制代码

一样的,咱们能够像建立 MainActivityComponentMainActivityModule 同样建立 DetailActivityComponentDetailActivityModule,所以具体步骤就略过了。

Fragment Components

若是在 DetailActivity 中有两个 Fragment,那咱们应该怎么办呢?实际上这一点都不难想到。先想一想 Activity 和 Application 之间的关系,Application 经过映射的 Module(在个人示例中就是ActivityBuilder)知道全部的 Activity,而且将全部的 Activity 做为子 Component 加入到 AppModule 中。

Activity 和 Fragment 也是如此,首先建立一个 FragmentBuilder Module 加入到 DetailActivityComponent 中。

如今咱们就能够像以前建立 MainActivityComponentMainActivityModule 同样来建立 DetailFragmentComponentDetailFragmentModule了。

DispatchingAndroidInjector

最后咱们须要作的即是注入到注入器中。注入器的做用是什么?我想用一段简单的代码解释下。

public class AndroidSampleApp extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        //simplified
    }

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return activityDispatchingAndroidInjector;
    }
}
复制代码

Application 拥有不少 Activity,这就是咱们实现 HasActivityInjector 接口的缘由。那 Activity 有多个 Fragment 呢?意思是咱们须要在 Activity 中实现 HasFragmentInjector 接口吗?没错,我就是这个意思!

public class DetailActivity extends AppCompatActivity implements HasSupportFragmentInjector {

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    //simplified
  
    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
}
复制代码

若是你没有子 Fragment 你不须要注入任何东西到 Fragment,那你也不须要实现 HasSupportFragmentInjector 接口了。可是在咱们的示例中须要在 DetailActivity 建立一个 DetailFragment

AndroidInjection.inject(this)

作这些都是为了什么?这是由于 Activity 和 Fragment 都不该该是如何被注入的,那咱们应该如何注入呢?

在 Activity 中:

@Override
protected void onCreate(Bundle savedInstanceState) {
 **AndroidInjection._inject_(this);**
    super.onCreate(savedInstanceState);
}
复制代码

在 Fragment 中:

@Override
public void onAttach(Context context) {
    **AndroidSupportInjection._inject_(this);
   ** super.onAttach(context);
}
复制代码

没错,恭喜你,全部工做都完成了!

我知道这有点复杂,学习曲线很陡峭,可是咱们仍是达到目的了。如今,咱们的类是不知道如何被注入的。咱们能够将所需实例对象经过 @Inject annotation 注解注入到咱们的 UI 元素。

你能够在个人 GitHub 主页找到这个工程,我建议你对照着 Dagger 2 的官方文档看。

iammert/dagger-android-injection _dagger-android-injection - Sample project explains Dependency Injection in Android using dagger-android framework._github.com

Dagger ‡ A fast dependency injector for Android and Java. A fast dependency injector for Android and Java.google.github.io

在第二部分,我想利用 Dagger 提供的新注解来简化 android-dagger 注入,可是在简化以前我想先给你们看看它原来的样子。

第二部分在这里了。

感谢阅读,祝你编码愉快!


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索