Dagger2和Android

时间:2016-11-11 09:40:18

标签: android dagger-2

我正在尝试将Dagger依赖注入实现到我的应用程序中,但我很难理解它是如何工作的,特别是来自Spring,其中DI更容易且更具说明性。

我想要做的是拥有一堆可以在我的应用程序中使用的注入就绪对象,即SharedPreferences,网络对象(OkHttp,Retrofit,Picasso ......),以及EventBus和SchedulerProvider对象对于RxJava。

This sample似乎提供了我需要的一切,但我无法掌握一些概念。

在上一页中引用的other sample中,他们创建了一个使用NetModule中提供的Retrofit对象的GithubService。为此,他们创建了一个像这样的GithubComponent:

@UserScope
@Component(dependencies = NetComponent.class, modules = GitHubModule.class)
public interface GitHubComponent {
    void inject(MainActivity activity);
}

他们正在使用UserScope注释来定义自己的范围。既然不能使用@Singleton,这是否意味着该对象不会是Singleton?范围如何真正影响DI?似乎他们只宣布一个命名范围没有更多的影响,但我不确定。

此外,我的应用程序是使用带有碎片的活动构建的。我是否必须为我的应用中的每个片段创建一个组件?即我需要在整个应用程序中使用我的REST api服务,我是否必须使用它们为每个屏幕声明一个组件?这会增加所需的样板代码量,因此听起来不太干净。

2 个答案:

答案 0 :(得分:8)

组件应该是"大DI提供商"它提供了特定范围的所有内容。

例如,您可以拥有SingletonComponent @Singleton范围,其中每个模块都添加了至少一个@Singleton范围提供商方法。

@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
    // provision methods
    OkHttpClient okHttpClient();
    RealmHolder realmHolder();
    // etc.
}

您可以为每个模块定义配置方法。

public interface DatabaseComponent {
    RealmHolder realmHolder();
}

public interface NetworkingComponent{
    OkHttpClient okHttpClient();
}

在哪种情况下你有

@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent 
      extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
    // provision methods inherited
}

在模块中,您可以指定工厂方法("提供程序方法"),指定如何创建特定类型的依赖项。

例如,

@Module
public class NetworkingModule {
    @Provides
    @Singleton
    OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()./*...*/.build();
    }

    @Provides
    @Singleton
    Retrofit retrofit(OkHttpClient okHttpClient) {
        // ...
    }
}

你可以把@Singleton范围想象成Spring会给你的大容器。

您还可以使用@Inject带注释的构造函数提供类的实例。这可以从组件中接收任何类,该类能够从该范围组件模块中的提供者方法实例化它(当然还有未编译的依赖项)。

@Singleton
public class MyMapper {
    @Inject
    public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
    }
}

@Singleton
public class MyMapper {
    @Inject
    RealmHolder realmHolder;

    @Inject
    OkHttpClient okHttpClient;

    @Inject
    public MyMapper() {
    }
}

然后,这将在组件中可用,您甚至可以为它提供配置方法,使其在组件依赖项中可继承:

@Singleton 
@Component(modules={...})
public interface SingletonComponent {
    MyMapper myMapper();
}

使用Dagger2,您还可以另外创建"子范围组件",它继承了从给定范围的组件提供的所有依赖关系。

例如,您可以继承所有@Singleton范围的组件,但您仍然可以根据新范围获得新的范围依赖关系,例如@ActivityScope

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

然后,您可以使用子组件或组件依赖项创建子范围组件。

  • 副成分:

@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
    MainPresenter mainPresenter();
}

然后可以在其父范围的组件中创建它:

@Singleton 
@Component(modules={...})
public interface SingletonComponent {
    MainActivityComponent mainActivityComponent(MainActivityModule module);
}

然后你可以使用单例组件来实例化它:

SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));
  • 组件依赖:

@ActivityScope
@Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
    MainPresenter mainPresenter();
}

要使其正常工作,您必须在超范围组件中指定配置方法。

然后您可以这样实例化:

SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
                       .singletonComponent(singletonComponent)
                       .mainActivityModule(new MainActivityModule(mainActivityHolder))
                       .build();

在Dagger2中,您可以通过以下方式获取依赖关系:

  • @Inject带注释的构造函数参数
  • 带有@Inject带注释的构造函数的类
  • @Inject带注释的字段
  • 来自@Component提供方法
  • 通过组件中定义的手动字段注入方法(对于您无法使用@Inject带注释的构造函数创建的类)

手动现场注入可能发生在您自己不能创建的MainActivity类。

手动字段注入仅注入您要注入的特定类。基类不会自动注入,他们需要在组件上调用.inject(this)

它的工作原理如下:

@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
}

然后你可以这样做:

public class MainActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
                          .singletonComponent(getSingletonComponent())
                          .mainActivityModule(new MainActivityModule(this))
                          .build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
        mainActivityComponent.inject(this);       
    }
}

答案 1 :(得分:0)

1)

  

由于不能使用@Singleton,这是否意味着对象会   不是单身人士?

GitHubComponent组件具有@UserScope范围,如果要在此范围内声明单例,则它应该是@Singleton。

2)

  

范围如何真正影响DI?看起来他们只是声明了一个   命名范围没有更多的影响,但我不确定。

来自javax docs

范围注释适用于包含注射剂的类 构造函数并控制注入器如何重用该类型的实例。 默认情况下,如果不存在范围注释,则注入器会创建一个 实例(通过注入类型的构造函数),使用实例 一次注射,然后忘记它。如果存在范围注释, 注射器可以保留实例以便稍后重复使用 注射。

3)

  

我是否必须为我的应用中的每个片段创建一个组件?

您可以创建一次,实例化并存储在Application对象中,然后在每次需要注入片段或活动时进行查询。

检查this example