3

I've an issue when using Dagger in multi module project. Let's say we have three modules.

  • core
  • newfeaturesmodule -> api project(":core")
  • oldfeaturesmodule -> api project(":newfeaturesmodule")

Application classes has same hierarchy either. The problem was both newfeaturesmodule and oldfeaturesmodule are using AndroidInjector method to inject activites. And it ended up with a crash saying activities on newfeaturesmodule has no android injector factory. The problem was when i call first application inject in newfeaturesmodule everything was just fine. And then child oncreate method of application class on oldfeaturesmodule called and it called inject for application again but these time it replaced the activity injectors on oldfeaturemodule with activity injectors on newfeaturemodule. So i came up with a custom solution like this

public abstract class CoreApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        CoreComponent component =
                DaggerCoreComponent.builder().coreModule(new CoreModule()).build();

        initDependencies(component);
    }

    public abstract void initDependencies(CoreComponent component);
}
public abstract class NewFeaturesApp extends CoreApp implements HasActivityInjector {

    public NewFeaturesComponent newFeaturesComponent;

    @Inject Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys;

    @Inject Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys;

    @Override
    public void initDependencies(CoreComponent component) {
        newFeaturesComponent = DaggerNewFeaturesComponent.builder().coreComponent(component).build();
        newFeaturesComponent.inject(this);
        keepAndroidInjectors(injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
        initLegacyDependencies(newFeaturesComponent);
    }

    public abstract void keepAndroidInjectors(
            Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys,
            Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys);

    public abstract void initLegacyDependencies(NewFeaturesComponent component);
}
public class OldFeaturesApp extends NewFeaturesApp {

    public OldFeaturesComponent oldFeaturesComponent;

    DispatchingAndroidInjector<Object> dispatchingAndroidInjector;

    @Inject Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys;

    @Inject Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys;

    Map<Class<?>, Provider<AndroidInjector.Factory<?>>> newInjectorFactoriesWithClassKeys;

    Map<String, Provider<AndroidInjector.Factory<?>>> newInjectorFactoriesWithStringKeys;

    @Override
    public void keepAndroidInjectors(
            Map<Class<?>, Provider<AndroidInjector.Factory<?>>> newInjectorFactoriesWithClassKeys,
            Map<String, Provider<AndroidInjector.Factory<?>>> newInjectorFactoriesWithStringKeys) {
        this.newInjectorFactoriesWithClassKeys = newInjectorFactoriesWithClassKeys;
        this.newInjectorFactoriesWithStringKeys = newInjectorFactoriesWithStringKeys;
    }

    @Override
    public void initLegacyDependencies(OldFeaturesComponent component) {
        oldFeaturesComponent = DaggerOldFeaturesComponent.builder()
                .application(this)
                .dependent(component)
                .build()
                .inject(this);

        Map<Class<?>, Provider<AndroidInjector.Factory<?>>> mergedInjectorFactoriesWithClassKeys = new HashMap<>();
        Map<String, Provider<AndroidInjector.Factory<?>>> mergedInjectorFactoriesWithStringKeys = new HashMap<>();

        mergedInjectorFactoriesWithClassKeys.putAll(newInjectorFactoriesWithClassKeys);
        mergedInjectorFactoriesWithClassKeys.putAll(injectorFactoriesWithClassKeys);

        mergedInjectorFactoriesWithStringKeys.putAll(newInjectorFactoriesWithStringKeys);
        mergedInjectorFactoriesWithStringKeys.putAll(injectorFactoriesWithStringKeys);

        dispatchingAndroidInjector = DispatchingAndroidInjector_Factory.newInstance(
                mergedInjectorFactoriesWithClassKeys, mergedInjectorFactoriesWithStringKeys);
    }

    public AndroidInjector<Object> androidInjector() {
        return dispatchingAndroidInjector;
    }
}

So i have two questions

  • Are there any way to merge activityInjectorFactories in Dagger?

  • And second, is my approach fine for this case or has it some downsides ? Could that be improved with providing these map dependencies from my NewFeaturesModule maybe, or something else ?

(Note: Dagger version is 2.27)

volsahin
  • 116
  • 5
  • This looks overly complicated. You don't really need separate application classes for each feature module. You should have one Application class and that should be very minimal since you're using dagger to inject dependencies and not a lot of classes should refer to the application class. [This](https://stackoverflow.com/a/58009976/2235972) approach is also valid for multi-module setup – denvercoder9 May 07 '20 at 00:46
  • Yes you're right but suppose that oldfeaturesapp is really old and the code inside of it is a total mess. So old application class has hundreds lines of codes, it has some static variables that may change functionality of app in some cases. It has its own services or sdks that decided to maintain but not to move forward to newfeatureapp. It has some behavioral code like sessions, push notifications, etc.. So separating them really makes sense. However combining them in dagger not a very easy task. Actually the main problem is with activityInjector on multi-module project @sonnet – volsahin May 07 '20 at 07:32
  • Currently I had the same issue at work (sessions, analytics, etc in CustomApplication). Instead of introducing multiple modules with dagger, we first cleaned up our CustomApplication class and split up its dependencies into separate classes. Every class that referenced this CustomApplication like `CustomApplication.logEvent(..)` or `CustomApplication.getSomeObject()` was also refactored first and moved out of the `CustomApplication` into its own class that was injected where necessary. Then we started introducing new modules and it's a smooth process now – denvercoder9 May 24 '20 at 22:12

0 Answers0