坚持尝试实现官方 Dagger 策略以避免繁琐的代码

Stuck attempting to implement the official Dagger strategy to avoid cumbersome code

在我努力遵循 the authors themselves 中关于注入和避免繁琐代码(我有)的好的官方建议时,我在尝试使用支持库时 运行 撞墙了。

根据文章:

AppCompat users should continue to implement AndroidInjector.Factory<? extends Activity> and not <? extends AppCompatActivity> (or FragmentActivity).

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

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 users should continue to implement AndroidInjector.Factory<? extends Activity>

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

编辑

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

FragmentKey methods should bind dagger.android.AndroidInjector.Factory<? extends android.app.Fragment>, not dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>.

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

更广泛地说,support Fragments do not extend base Fragments (which are deprecated anyway in API 28 and beyond). This paints them in contrast to AppCompatActivity and its superclass, the support library's FragmentActivity, which both extend the framework Activity as introduced in Android API level 1. Thus, whether you're using support Fragments or built-in Fragments, you might not have a parent AppCompatActivity, but you'll always have an Activity of some sort. This is important because Android reserves the right to instantiate your Fragment using its necessary public no-arg constructor,这意味着片段只能使用它可以在 onAttach 中找到的东西进行自我注入(即它的父片段,它的 Activity ,或其应用程序)。

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

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