【问题标题】:Stuck attempting to implement the official Dagger strategy to avoid cumbersome code卡住试图实现官方 Dagger 策略以避免繁琐的代码
【发布时间】:2018-07-12 08:06:34
【问题描述】:

在努力遵循来自the authors themselves 的关于注入和避免繁琐代码(我有)的良好和官方建议时,我在尝试使用支持库时遇到了障碍。

根据文章:

AppCompat 用户应继续实施AndroidInjector.Factory<? extends Activity> and not <? extends AppCompatActivity>(或FragmentActivity)。

我坚持使用视图始终为 Fragments 的 MVP 架构,我不想让我的 Activity 参与任何 DI 业务,但我想知道这是否有必要工作,但到目前为止我没能。如果我跳过整个支持,应用程序会在运行时崩溃,因为片段的实例是支持的(以防它不明显)。然后我开始尝试实现HasSupportFragmentInjector 而不是HasFragmentInjector 的任务,由于编译错误,为了我的心理健康,我的大脑已经忘记了这一点。过了一会儿,我开始思考非支持活动如何托管支持片段。啊!那些棘手的通配符。但是无论我如何尝试遵循建议,我都无法想出没有EmptyModule 的方法,我还需要在 Activity 中进行设置,以便通过 dagger 及其 (真的,对我来说仍然是魔法)。为什么我没试过?我也可以,但我厌倦了无望的变化,此时我需要帮助。

AppModule.kt

@Singleton
@dagger.Module
class AppModule(val application: Application) {
    @Provides @Singleton fun application(): Application = application
    ...
}

AppComponent.java

@ApplicationScope
@Singleton
@Component(modules = {
        AndroidSupportInjectionModule.class,
        ...
        FooFragmentModule.class,
})
public interface AppComponent {
    Application app();
    ...
    void inject(MyApp app);
}

MyApp.java

public class MyApp extends Application implements HasActivityInjector {

    private AppComponent component;
    public AppComponent someWayToReturnAppComponent() {
        ...
    }

    @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    @Override
    public void onCreate() {
        component = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                // more app-scoped modules
                .build();

        component.inject(this);
    }


    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

}

MainActivity.java

public abstract class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getLayout()); // inflate the fragment via XML here
    }

    @Inject DispatchingAndroidInjector<Fragment> dispatchingFragmentInjector;

    @Override
    public AndroidInjector<Fragment> fragmentInjector() {
        return dispatchingFragmentInjector;
    }
}

FooFragmentComponent.java

@Subcomponent
public interface FooFragmentComponent extends AndroidInjector<FooFragment> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<FooFragment> {}

}

FooFragmentModule.kt

@dagger.Module(subcomponents = {FooFragmentComponent.class})
public abstract class FooFragmentModule {

    @Binds
    @IntoMap
    @FragmentKey(FooFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> bindFragmentInjectorFactory(FooFragmentComponent.Builder builder);

    @ActivityScope
    abstract FooFragment contributeFooFragmentInjector();

    @Provides
    static FooPresenter presenter() {
        return new FooPresenter();
    }
}

FooFragment

public class FooFragment extends Fragment implements SomeView {

    @Inject FooPresenter presenter;

}

好的。至此,再回到

AppCompat 用户应继续实施AndroidInjector.Factory&lt;? extends Activity&gt;

我没有必要(并且愿意反对)使用它,仅用于片段。我真的需要为它设置一个模块和组件还是我遗漏了什么?

编辑

在遵循 EpicPandaForce 的使用 AndroidSupportInjectionModule 的建议后,Dagger 现在抱怨说

FragmentKey 方法应该绑定dagger.android.AndroidInjector.Factory&lt;? extends android.app.Fragment&gt;,而不是dagger.android.AndroidInjector.Factory&lt;? extends android.support.v4.app.Fragment&gt;

【问题讨论】:

  • 使用AndroidSupportInjectionModule
  • @EpicPandaForce 我做了,但仍然遇到问题。感谢您提供进一步的反馈。

标签: android dependency-injection android-support-library dagger


【解决方案1】:

正如 @EpicPandaForce 在 cmets 中提到的,您需要使用 AndroidSupportInjectionModule 来支持 Fragments。 您还需要使用the FragmentKey in dagger.android.support,而不是the one in dagger.android这应该可以帮助您解决编辑中的问题。

更广泛地说,support Fragments 不要扩展 base Fragments(无论如何在 API 28 及更高版本中已弃用)。这将它们与AppCompatActivity 及其超类the support library's FragmentActivity 形成对比,它们都扩展了Android API 级别1 中引入的框架Activity。因此,无论您使用的是支持Fragments 还是内置Fragments,您可能没有有一个父 AppCompatActivity,但你会总是有某种活动。这一点很重要,因为 Android reserves the right to instantiate your Fragment using its necessary public no-arg constructor,这意味着 Fragment 只能使用它可以在 onAttach 中找到的东西(即它的父 Fragment、它的 Activity 或它的应用程序)进行自我注入。

dagger.android 并不关心您的 Activity 是否是 AppCompatActivity,因为 它除了寻找自己的注入器之外不使用 Activity。您可以在AndroidSupportInjection.findHasFragmentInjector 私有方法中看到这一点,该方法检查(按顺序)父片段的层次结构,然后是活动,然后是应用程序。因此,尽管实际上支持片段只能在支持活动上正常运行,但 dagger.android 可以基于超类 Activity 绑定其键,因为没有理由区分它们并设置两个单独的映射(Activity 与 AppCompatActivity)。即使有这样的分离,您也可以将 AppCompatActivity 注入器绑定到您的 Activity 映射中,一切都会变得非常混乱。

您还应该从该搜索顺序中获取 如果您没有 Activity 范围的绑定,则不需要创建 Activity 范围的组件;您可以让您的应用程序实现 HasSupportFragmentInjector,将您的 FooFragmentModule 直接安装到 AppComponent 中,并从您的 MainActivity 中删除 HasSupportFragmentInjector。这是非典型的,只是因为大多数应用程序对应该可注入的 Activity 状态或控制器有某种意义(即使只是注入 Activity 实例本身,或其上下文或资源)。如果您只有 @ActivityScope 注释,因为您正在尝试使其工作,您可以完全跳过该步骤,只使用一个 Application 组件和几个 Fragment 子组件。但是,我认为您最终很可能需要@ActivityScope,因此尽早为其创建一个组件是非常合理的。

【讨论】:

  • 我不敢相信我被导入打败了,但没有检查。感谢这个惊人的解释,但 Dagger 的文档与其惊人的本质正好相反还是只是我?
  • @Chisko 很高兴您发现答案很有帮助!我不是技术作家或 Dagger 开发人员,我并不是要贬低我的同事,但似乎确实存在比实际解释或决策更多的技术级文档。遇到冗长但难以理解的错误消息也很容易。我更受欢迎的一篇文章explains @Reusable vs @Singleton,这可能是一种熟悉的体验,因为官方文档在技术上是正确的,但(如问题中所引用的)非常不透明。
  • 我浏览了更多的文章,现在我对 Dagger 有了更多的了解。但现在令我震惊的是,我试图注入的(目前)唯一依赖项是空的。再次查看您的答案,我现在想知道为什么我需要创建一个活动组件(或片段),而本文的整个想法是摆脱生命周期样板(根据它,这本身就是组件创建) .我尝试将inject(FooFragment) 方法添加到子组件和...
  • 使用@ActivityScope abstract FooFragment conntributeFooFragmentInjector(); 并在@Provides static FooPresenter presenter() { return new FooPresenter();} 上使用多个注释,如您建议的@Reusable,但它始终为空。很高兴知道使@Inject 工作的过程。
  • 我在片段中遗漏了AndroidInjection.inject(this);,以及我希望将所有活动集成到一个文件中的活动模块的一个小技巧。但现在一切正常。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 2011-03-19
  • 1970-01-01
  • 2016-04-14
  • 2015-01-02
  • 1970-01-01
相关资源
最近更新 更多