Dagger2注入了很多活动/片段/服务(可能获得大量NPE)

时间:2016-10-23 19:14:34

标签: 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();
    }
}

在我想要注入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必须提供所有活动/片段/服务(等),我想使用注入。但我不能使用像Activity / Fragment这样的通用对象。或者我真的不在乎现实,也不明白Dagger2是如何运作的?

因为这对于拥有大约50多项活动和大量片段/服务的项目来说真的很疯狂......

@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);那样使用它时,如何提供注入泛型类,因为它需要特定的对象。所以我必须在ApplicationComponent内写下所有这些对象,而不是使用?表示法?

这不仅仅是疯狂:(。也许更好的留在RoboGuice,这是更开发人员友好,不需要这个开销和手动检查每个注入的对象?当我忘了添加到这个列表,我会在运行时获取NPE(当我不会对它进行大量测试时,它会使客户崩溃)。

手动编写它比在不可能使用Activity / Fragment / Service这样的通用对象时创建所有对象的列表要快得多。

是否有更好的解决方案,当我不想使用相同的通用BaseActivity时会注入ApplicationModule的每个部分,并且每个活动都会被这个巨大的BaseActivity扩展?< / p>

2 个答案:

答案 0 :(得分:2)

此问题涉及投诉方面,但尝试回答:

  

我真的很困惑接口ApplicationComponent,它必须提供我想要使用注入的所有活动/片段/服务(等)。但我不能使用像Activity / Fragment这样的通用对象。或者我真的不在乎现实,也不明白Dagger2是如何运作的?

这确实是Dagger 2的工作原理;它必须静态地提供进样器(组件)内注射目标的类型,并且不能使用'通用'(协变)类型。 Dagger 2这样做是为了维持100%静态的DI框架。

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

  

也许最好留在RoboGuice,这对开发人员更友好,不需要花费这个开销并手动检查每个注入的对象

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

  

是否有更好的解决方案,当我不想使用相同的通用BaseActivity时,它会注入ApplicationModule的每个部分,并且每个活动都会被这个巨大的BaseActivity扩展?

嗯,这将取决于您使用的依赖项和位置。如果你有一个小的依赖关系列表,你想要到处,那么这可能是最好的解决方案,即制作一个BaseActivity来接收这些依赖关系并使其可用于你的所有子类。或者,您可以使用子组件和模块来划分对象图,以便您可以将使用者/注入目标与正确的模块组合在一起。然后你不需要有一个列出所有注射部位的“神”组件。

  

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

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

如果绝对必须是注射目标,则需要子类:NotificationHelper<String>Notification<Integer>成为StringNotificationHelper extends NotificationHelper<String>IntegerNotficationHelper extends NotificationHelper<Integer>。这是“清洁代码”一书中推荐的做法。

您不需要在ApplicationComponent中写入所有注入站点,您可以创建与项目中依赖项的使用模式相对应的子组件。

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

答案 1 :(得分:1)

谢谢,我们就像你一周前描述的那样解决了它。使用每个对象注入。

更好的解决方案是不要只使用inject但复杂的名称。为什么?因为它有助于解决为什么没有注入某些对象(你知道,基类等)。

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
    void injectApplication(@NonNull Application app);
    void injectMainActivity(@NonNull MainActivity object);
    void injectDispatcherActivity(@NonNull DispatcherActivity object);
    ...
}

我们最终使用的是通用性UtilityWrapper,如下所述:https://medium.com/@patrykpoborca/dagger-2-and-base-classes-generics-and-presenter-injection-7d82053080c#.b58ykd4cm