Dagger: Scalability issues with dagger-android for scoped modules

Created on 9 Mar 2018  路  3Comments  路  Source: google/dagger

I have 3 types of scopes ApplicationScope (Singleton), ActivityScope and FragmentScope. There were a couple of fragments which needed Fragment scoped dependencies, Activity scoped dependencies and Application scope dependencies. So, if there were 2 fragments which were hosted in the same activity, we wanted that the fragments should share the same activity dependencies. To make this work we followed the suggestion given bt PhilGlass on https://github.com/google/dagger/issues/796. This works perfectly fine for small project and places where small number of fragments are hosted in the activity. Once the number of fragments hosted in the activities increase, the compile time and DaggerApplicationComponent file size starts increasing exponentially.
For example, let's say we have 10 activities (Activity1...Activity10) and we have 20 fragments (Fragment1...Fragment20). Now, in my application it is required that any fragment can be launched in any activity. So, to correctly scope the activity dependencies, this is how we end up configuring the Builder modules:

FragmentBuildersModule:

@Module
@FragmentScope
public abstract class FragmentBuildersModule {

    @Module
    abstract class Fragment1Module {
        @Binds
        abstract Fragment fragment(@NonNull Fragment1 fragment1);
    }

    @FragmentScope
    @ContributesAndroidInjector(modules = Fragment1Module.class)
    abstract Fragment1 fragment1();

    .
    .
    .

    @Module
    abstract class Fragment20Module {
        @Binds
        abstract Fragment fragment(@NonNull Fragment20 fragment20);
    }

    @FragmentScope
    @ContributesAndroidInjector(modules = Fragment20Module.class)
    abstract Fragment20 fragment20();
}

ActivityBuildersModule:

@Module
@ActivityScope
public abstract class ActivityBuildersModule {

    @Module
    abstract class Activity1Module {

        @Binds
        abstract Activity activity(@NonNull Activity1 activity1);
    }

    @ActivityScope
    @ContributesAndroidInjector(modules = {Activity1Module.class, FragmentBuildersModule.class})
    abstract Activity1 activity1();

    .
    .
    .

    @Module
    abstract class Activity10Module {

        @Binds
        abstract Activity activity(@NonNull Activity10 activity10);
    }

    @ActivityScope
    @ContributesAndroidInjector(modules = {Activity10Module.class, FragmentBuildersModule.class})
    abstract Activity10 activity10();
}

Now, when we compile this project, we will see that the DaggerApplicationComponent file is huge (almost 14K lines) and more than 400 internal classes - which may be too high for a project like this. Also there are around 30 more generated files - ActivityBuildersModule_Activity1, ActivityBuildersModule_Activity2...ActivityBuildersModule_Activity10, FragmentBuildersModule_Fragment1, FragmentBuildersModule_Fragment2...FragmentBuildersModule_Fragment20.

Also for this specific example, found out that the "_FragmentSubcomponentBuilder_" class is generated 10 times in DaggerApplicationComponent. Basically "_Fragment1SubcomponentBuilder_" class is generated for each and every Activity, "_Fragment2SubcomponentBuilder_" class is also generated for each and every activity and so on.
Because of this, as we add more fragments and activities, the compile time and DaggerApplicationComponent size increase exponentially. When the number of activities are 30 and when the fragments are 100, the DaggerApplicationComponent file size is around 20 MBs and compile time also increased a lot.

Here is the demo project (https://github.com/sagarwaghmare07/dagger-android-demo/) which has 10 activities and 20 fragments in the configuration mentioned above.

Questions:

  1. Is there a better way to achieve this required behavior? (any fragment can be launched in any activity and still the dependencies should be correctly provided).
  2. Why do we generate separate "Fragment1SubcomponentBuilder", "Fragment1SubcomponentImpl" etc for each and every activity. Can we generate it once and just use it wherever required?

Most helpful comment

We're working on something to generate subcomponents on their own, but it's a large project which will take some time. I don't imagine it will be ready in the near future.

As for the N x M relationship of fragments by activities... there's no better way within Dagger to implement these types of relationships. Maybe you should reconsider your architecture - do all N activities really need all M fragments? And do all fragments always need bindings from their parent activities?

All 3 comments

Hi @ronshapiro , this is blocking our switch to dagger-android, please have a look at your early convenience, thanks!

We're working on something to generate subcomponents on their own, but it's a large project which will take some time. I don't imagine it will be ready in the near future.

As for the N x M relationship of fragments by activities... there's no better way within Dagger to implement these types of relationships. Maybe you should reconsider your architecture - do all N activities really need all M fragments? And do all fragments always need bindings from their parent activities?

@ronshapiro Why is this task closed? Should we expect some new code generator for Subcomponents that will solve the scalability problem or is there another issue?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JakeWharton picture JakeWharton  路  3Comments

mskx42 picture mskx42  路  3Comments

blackberry2016 picture blackberry2016  路  3Comments

SAGARSURI picture SAGARSURI  路  3Comments

peter-tackage picture peter-tackage  路  3Comments