Android |匕首2.根据条件的不同,将不同的子类注入片段



我正在使用MVP和dagger2 di。我有一个片段,可以在一些活动中重复使用。MVPPRESENTER表示,我有一个用于主持人的接口类型作为片段的属性。根据使用片段的活动,我需要将不同的演示者注入其中(每个演示者都是MVPPRESENTER的实现)。因此,我需要一种方法将MVPPRESENTER的每个实现都注入片段中。

当前,我有一个可怕的解决方案,它有效,但这只是错误的,并且创建了从未使用过的不必要的对象。这是代码:

public class MyFragment {
...
@Inject
public void setPresenter(@NonNull ProfilePresenter presenter) {
    if (mAdapter instanceof ProfileAdapter) {
        this.presenter = presenter;
    }
}
@Inject
public void setPresenter(@NonNull ContactsPresenter presenter) {
    if (mAdapter instanceof ContactsAdapter) {
        this.presenter = presenter;
    }
}
...
}

这是我的模块:

@Module
class PresentersModule {
@Provides
@Singleton
ProfilePresenter ProfilePresenter() {
    return new ProfilePresenter();
}
@Provides
@Singleton
ContactsPresenter ContactsPresenter() {
    return new ContactsPresenter();
}
}

您看到,根据适配器类型,我分配了主持人或不分配。我知道这是愚蠢的。问题是dagger需要精确的类型才能指定注入,并且接口类型无法工作。处理此类情况的正确方法是什么?

您所看到的三种不同程度的解决方案。

现在,注入两个选择:如果您知道所有片段的用例您可以使用与现在的类似方法轻松地这样做。我的变体使用提供程序,这些提供程序会自动为图中的任何对象绑定,因此您不会不必要地创建对象的全树;另外,@inject方法可以获取任意参数列表,因此您可以选择一个方法,以一种方法进行。

@Inject
public void setPresenter(
        @NonNull Provider<ContactsPresenter> contactsPresenterProvider,
        @NonNull Provider<ProfilePresenter> profilePresenterProvider) {
    if (mAdapter instanceof ContactsAdapter) {
        this.presenter = contactsPresenterProvider.get();
    } else if (mAdapter instanceof ProfileAdapter) {
        this.presenter = profilePresenterProvider.get();
    }
}

其他两个解决方案涉及多个组件:您没有说"将我的图形结合在一起",而是有效地要求Dagger为您生成多个选项,这意味着您的图形可以变化很大,但保持一致。如果您以不同的方式重复使用对象,则此技术可能会更有用em>不同的 D.为了始终支持两个深图形,儿童组件是一个更好的选择。

使用组件依赖项:如RST的答案,您可以使用组件依赖项来隔离片段。他们在解释方面做得很好,所以我不会在这里重复一遍。但是,您应该知道,组件依赖性只能消耗在您所依赖的组件上暴露的绑定:即使Foo和bar绑定在Dicomponent上,您也无法从profilecomponent或ContactScomponent访问它们,除非您将Foo getFoo()Bar getBar()放在您的dicomponent上。(也就是说,组件依赖性也不一定是dagger组件;它们可以是您实施自己的任意类型或让Dagger为您实施。)

使用子组件:虽然第一个提及子组件,但我认为它们需要更多的解释,尤其是因为它们是最近发行的dagger的核心组成部分。UI零件可能很难与组件依赖项一起提取 - 隐式和自动从周围的组件中继承绑定,因此您不必明确地暴露于dicomponent上的绑定。在此问题上查看其他差异。

@Component
public interface DiComponent {
    ProfileComponent getProfileComponent();    // Dagger generates implementations
    ContactsComponent getContactsComponent();  // as part of DiComponent.
}
@Subcomponent(modules={ContactsModule.class})
public interface ContactsComponent {
    void inject(MyFragment myFragment);
}
@Module
public interface ContactsModule {
    @Binds MvpPresenter bindMvpPresenter(ContactsPresenter contactsPresenter);
}
@Subcomponent(modules={ProfileModule.class})
public interface ProfileComponent {
    void inject(MyFragment myFragment);
}
@Module
public interface ProfileModule {
    @Binds MvpPresenter bindMvpPresenter(ProfilePresenter profilePresenter);
}

在上面,root diComponent对mvppresenter没有绑定,因此它本身不能注入我的fragment。但是,ProfileComponent和ContactScomponent可以使用,并且每个都将使用在相应的模块中配置的不同图表(但从DiComponent的模块中默默遗传了共同的绑定)。如果图形的变化较大,则像每个MVPPRESENTER一样使用相同的验证器,但使用不同的profileValidationRule与ContactsValidationRule,则可以将验证程序绑定到不同模块中的这些不同类别以获得不同的行为。

> > >

(为了完整性,您通常还可以选择使用AutoFactory之类的工厂并将像主持人这样的参数传递给您的特定容器,例如Fragment。,当Android强迫零ARG公共构造函数时,它并不是真正的选择,因此它可以随意创建碎片实例。)

浏览您给MVP-Presenters的名称,可以得出结论,他们的互补MVP视图宁愿分开并以不同的片段实现。

但是,如果您想维护物品,则只有单个setPresenter方法在您的片段中声明,可能是处理问题的最简单方法是引入带有互补模块的单独组件,以提供理想的演示者实现。p>,要使此解决方案工作,您需要调整片段以包含setPresenter方法的单个用作MVPPresenter的声明作为参数:

@Inject
public void setPresenter(@NonNull MVPPresenter presenter) {
    this.presenter = presenter;
}

之后,您需要提供公开inject(...)方法并声明适当模块的组件。由于这些依赖图将取决于 main 组件实例,因此它们应获得自己的范围(与活动或片段相关,具体取决于实际上的类别对象)。

例如,如果您使用的是DiComponent用于通过@Singleton注释定义的所有依赖项,则需要声明@MyFragmentScope注释并提供依赖上述DiComponent的组件,以声明可注射的演讲者:<<<<<<<<<<<<<<

import javax.inject.Scope;
@Scope
public @interface MyFragmentScope {
}

您的依赖组件看起来像:

@MyFragmentScope
@Component(dependencies = DiComponent.class, modules = ProfileModule.class)
public interface ProfileComponent {
    void inject(MyFragment fragment);
}

使用互补模块:

@Module
public class ProfileModule {
    @Provides
    @MyFragmentScope
    MVPPresenter providesProfilePresenter() {
        return new ProfilePresenter();
    }
}

注意:返回类型是MVPPresenter,不是具体实现。

类似地,您需要为ContactsPresenter创建ContactsComponentContactsModule

最终,您应该使用适当的组件实例执行注射。现在而不是使用

diComponent.inject(myFragment)

您应该使用将提供理想依赖性的组件。

此时,您实际上将使用 switch 定义应使用哪个主持人。如果要使用ProfilePresenter,则需要使用:

DaggerProfileComponent.builder()
        .diComponent(diComponent)
        .build()
        .inject(myFragment);

或在ContactsPresenter注入的情况下,您需要使用:

DaggerContactsComponent.builder()
        .diComponent(diComponent)
        .build()
        .inject(myFragment);

使用单独的组件在较小的应用程序(例如活动)中使用单独的组件是相当普遍的做法。可以将这些组件声明为常规依赖性组件或子组件(请参阅@Subcomponent文档以获取参考)。从dagger2.7开始,有一种新的方式可以通过@Module.subcomponents声明子组件。由于这个事实,有机会将AppComponent与活动子组件分解。您可以从FrogerMC中参考示例GitHub存储库以供参考。他还有一篇关于此主题的很棒的补充博客文章。

最新更新