16

I am using aproach to provide modules to Dagger described in answer of this question Android Customize Workflow for Product Flavors

Our approach is a bit different - we have Modules in debug build type in src/debug/java and in release build type in src/release/java. Our main module is called ApplicationModule it includes all other.

Modules in src/debug/java provides some custom behaviour for debugging, logging etc and overrides ApplcationModule.

Now we have a need to have custom behaviour based on application flavor.

What would be correct approach to do that?

For example flavors A to C should provide custom behaviours, while flavors D to F should provide basic, default behaviour.

So far I came up with such.

  • All flavors (not build types) has same class in src/flavorX/java/com.application.FlavorModule
  • To avoid code duplication only flavors A to C provide custom behavior while other completely empty so that project would compile. And default behaviour is provided my module in src/main/java

Is there a better way to achieve such result? Because I don't like empty src/flavorX/java/com.application.FlavorModule and don't like code duplication...

Community
  • 1
  • 1
Martynas Jurkus
  • 9,231
  • 13
  • 59
  • 101

2 Answers2

18

Little complicated but here's how I did it:

Create an interface MainComponent and keep it in src/main, this should contain anything that is not flavor specific

public interface MainComponent {
    void inject(MyApplication o);

    void inject(BusinessObject o);

    Foo getFoo();

    Activitycomponent plusActivityComponent(ActivityModule activityModule);

}

Within each flavor create an interface that inherits from the above one

public interface FlavorComponent extends MainComponent {
//flavor specific injection stuff similar to SourceComponent
}

Within Debug/Beta/Release create the actual component and extend the FlavorComponent (giving you all the flavor specific niceties).

@Singleton
 @Component(modules = {ApplicationModule.class, FlavorModule.class,
         BetaApplicationModule.class, AnotherModuleJustBecause.class})
public interface ApplicationComponent extends FlavorComponent {
     void inject(NYTApplication a);

 }

Notice that you can also include a flavor specific FlavorModule that can be different in each flavor or not include it in Release while including it in Beta.

Also include a ComponentFactory within Debug/Beta/Release returning the common Flavor Component Interface

public class ComponentFactory {

public static final FlavorComponent getComponent(Application context) {
        return DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(context))
                .build();
    }

and finally from your Application class call:

ComponentFactory.getComponent(this).inject(this);

The component factory will return the Build Type Component which will extend the Flavor's Component.

FriendlyMikhail
  • 2,857
  • 23
  • 39
  • 1
    Looking at your `ComponentFactory`, I don't understand why your last line of code isn't `ComponentFactory.getComponent(this).inject(this)`. Could you explain? – Kevin Krumwiede Dec 08 '15 at 20:08
  • This might make it more clear by reading the article: https://medium.com/@theMikhail/flavoring-your-dagger-14ab8bcef9f8 – Juan Mendez Jan 05 '18 at 02:46
0

My solution was an adaptation of this article cited above

I have an interface call ApplicationComponent in src/main:

    @Singleton
    @Component(modules = [

        FlavorModule::class,

        Anyothermoduleyouhave::class

    ])

    interface ApplicationComponent {}

Now, inside of each flavor folder, you have to create 2 files:

  1. FlavorModule (an abstracted class to call your config)
  2. FlavorConfig (the config or whatever specific flavor code you need)

by the way: if you don't have any source folder you can create by adding on your gradle file:

        java.srcDirs += ['src/main/kotlin', 'src/myFlavorCode/kotlin']

and creating the kotlin folder as well

FlavorModule:

    @Module
    class FlavorModule {

        @[Provides Singleton]
        fun getConfig(config: MySpecificFlavor1Config): FlavorConfig

    }

FlavorConfig

    class MySpecificFlavor1Config @Inject constructor() : FlavorConfig {
        override fun getMyValueThatImLookingFor() = "flavor 1 woooooow"
    }

Do the same for the Flavor 2

Important: to make this work dynamically with dagger, use the same package path for both FlavorModule/FlavorConfig classes, like package br.com.main.config, this way dagger understands which FlavorModule to use

Igor Romcy
  • 336
  • 4
  • 7
  • 1
    I don't really understand why we need to use `abstract class`, `abstract fun` and `@[Binds Singleton]` for the flavor module. If we can just use `class`, `fun` and `@Provides` respectively – Dannie Mar 10 '21 at 16:55