android - Dagger2 注入(inject)大量 Activity/fragment/服务(可能获得大量 NPE)

标签 android dependency-injection nullpointerexception dagger-2

我们使用了 RoboGuice,但它已被弃用,我开始用 Dagger2 替换它。

// https://github.com/google/dagger
compile('com.google.dagger:dagger:2.7')
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
provided 'org.glassfish:javax.annotation:10.0-b28'

@Module
public class ApplicationModule {
    Application mApp;

    public ApplicationModule(@NonNull Application app) {
        Preconditions.checkNotNull(app);
        mApp = app;
    }

    @Provides
    @Singleton
    public SharedPreferences providesSharedPrefs() {
        return PreferenceManager.getDefaultSharedPreferences(mApp);
    }

    @Provides
    @Singleton
    public DateHelper providesDateHelper() {
        return new DateHelper(mApp);
    }

    @Provides
    @Singleton
    public PersistentConfig providesPersistentConfig() {
        return new PersistentConfig(mApp);
    }

    @Provides
    @Singleton
    public OttoBus providesOttoBus() {
        return new OttoBus();
    }
}

public class Application extends MultiDexApplication {
    private ApplicationComponent mApplicationComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        mApplicationComponent.inject(this);
    }

    public static Application getApp(@NonNull Context context) {
        return (Application) context.getApplicationContext();
    }

    public static ApplicationComponent getApplicationComponent(@NonNull Context context) {
        return getApp(context).getApplicationComponent();
    }
}

当我想注入(inject) ApplicationComponent 时,在各处之后

例如MainActivity

public class MainActivity extends AppCompatActivity {
    @Inject
    PersistentConfig mPersistentConfig;

    @Inject
    OttoBus mOttoBus;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Helper.manageRotation(this);
        super.onCreate(null);
        setContentView(R.layout.main_layout);
        Application.getApplicationComponent(this).inject(this);
    }
}

Application.getApplicationComponent(context).inject(this);

第一个问题:我对 interface ApplicationComponent 真的很困惑它必须提供我想要使用注入(inject)的所有 Activity/fragment/服务(等)。但我不能使用像 Activity / Fragment 这样的通用对象。还是我真的脱离了现实,不明白Dagger2是如何工作的?

因为这对于拥有大约 50 多个 Activity 和大量 fragment/服务的项目来说真的很疯狂......

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
    void inject(@NonNull Application app);
    void inject(@NonNull MainActivity object);
    void inject(@NonNull DispatcherActivity object);
    void inject(@NonNull DateTimeHelper object);
    void inject(@NonNull DatabaseHelper object);
    void inject(@NonNull LandingPageActivityFragment object);
    void inject(@NonNull RedirectActivity object);
    void inject(@NonNull CategoryFragment object);
    void inject(@NonNull BaseModuleFragment object);
    void inject(@NonNull NotificationHelper object);
    void inject(@NonNull RecordAdapter object);
    void inject(@NonNull PagingProvider object);
    void inject(@NonNull FilterDialog object);
    ... next 100+ injections?
}

我说,这不可能是真的......

第二个问题:当我无法像void inject(@NonNull NotificationHelper<? extends GenericObject> object);那样使用它时,我如何提供注入(inject)泛型类?因为它需要特定的对象。所以我必须将所有这些对象写在 ApplicationComponent 内并且不使用 ?符号?

这不仅仅是疯狂:(。也许最好还是使用 RoboGuice,它对开发人员更加友好,并且不需要进行这种开销并手动检查每个注入(inject)的对象?当我忘记将它们添加到此列表中时,我会在运行时获取NPE(当我不会对其进行大量测试时,它会让客户崩溃)。

当无法使用像Activity / Fragment / Service这样的通用对象时,手动编写比创建所有对象的列表要快得多。 .

当我不想使用相同的通用 BaseActivity 时,是否有更好的解决方案它将注入(inject) ApplicationModule 的每个部分每个 Activity 都将由这个巨大的BaseActivity来扩展?

最佳答案

这个问题有一些投诉的方面,但要尝试回答:

I'm really confused about interface ApplicationComponent which must provide all activities/fragments/services (etc) where I want to use injection. But I can't use generic objects like Activity / Fragment. Or am I really out of reality and don't understand how Dagger2 works?

这确实是 Dagger 2 的工作原理;如果您必须在注入(inject)器(组件)内静态提供注入(inject)目标的类型,并且不能使用“通用”(协变)类型。 Dagger 2 这样做是为了保持 100% 静态的 DI 框架。

请注意,您正在指定 RecordAdapterDatabaseHelper作为注入(inject)部位。您可能不需要这样做,您应该尝试仅指定构造函数不可见的顶级对象( ActivityFragmentService )作为注入(inject)站点。其余对象应该能够通过使用 @Inject 注释它们的依赖关系来构造。并在 Module 中指定它们的依赖关系(如果有) .

Maybe better stay with RoboGuice which is much more developer friendly and don't need make this overhead and manual check every injected objects

是的,Roboguice 更友好,因为您不必担心指定注入(inject)目标。但是,请在 Roboguice 中考虑以下因素: 1. 当您错误地设置对象图时,您会得到“红色死亡堆栈跟踪” 2. 事实上,您无法通过“查找用法”查看项目中实际使用了哪些接口(interface)实现,这也可能是“对开发人员不友好”

Is there a better solution, when I don't want use same generic BaseActivity which will inject every part of ApplicationModule and every activity will be extended by this huge BaseActivity?

嗯,这取决于您正在使用哪些依赖项以及在何处使用。如果您有一小部分依赖项想要到处注入(inject),这可能是最好的解决方案,即创建 BaseActivity接收这些注入(inject)并使其可供所有子类使用。或者,您可以使用子组件和模块来划分对象图,以便可以将消费者/注入(inject)目标与正确的模块分组在一起。那么您就不需要拥有一个列出所有注入(inject)部位的“上帝”组件。

Second question: How I can provide to inject generic classes, when I can't use it like void inject(@NonNull NotificationHelper object); because it require specific object. So I must write all this objects inside ApplicationComponent and not use ? notation?

是的,您必须提供注入(inject)目标的不变类型。我不确定你的NotificationHelper<String>是否是顶级类型。当您注入(inject) Fragment 时,为什么不通过对象图注入(inject)它? , ActivityService

如果它绝对必须是一个注入(inject)目标,您将需要子类化:NotificationHelper<String>Notification<Integer>成为StringNotificationHelper extends NotificationHelper<String> , IntegerNotficationHelper extends NotificationHelper<Integer> 。这是《Clean Code》一书中推荐的做法。

您不需要将所有注入(inject)站点都写入ApplicationComponent内,您可以创建与项目中依赖项的消费模式相对应的子组件。

(披露:作为目前正在尝试将项目从 Roboguice 迁移到 Dagger 2 的人,我对您的投诉表示同情)

关于android - Dagger2 注入(inject)大量 Activity/fragment/服务(可能获得大量 NPE),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40206831/

相关文章:

android - 使用imagepicker加载图像时, ionic 应用程序崩溃

android - 如果某些链接 View 约束到另一个链接 View ,则 ConstraintLayout 链不起作用

symfony - 如何在 Symfony 5 迁移中使用容器(和服务)?

c# - CompilationUnit 对象是否应该具有保存自身并编译到磁盘的方法?

java - 为什么对对象使用 getter 会返回 NullPointerException

java - 在 Activity 之间传递对象和/或对象列表的有效方法是什么?

Android 默认字符编码

asp.net-mvc-3 - 依赖注入(inject)调用了太多次?

java - 什么是 NullPointerException,我该如何解决?

java - Java Swing 中的空指针异常