0

I'm working on an Android App which follows the MVP architecture pattern. In all my fragments I'm injecting a Presenter. Therefore my Fragments (Views) need to have a Component in which I declare the injects. For example:

@ActivityScope
@Component(
        dependencies = AppComponent.class,
        modules = {
                PresenterModule.class,
                InteractorModule.class
        }
)
public interface ViewInjectorComponent {
    void inject(SelectEventOccurrenceFragment fragment);
    void inject(CreateOpponentFragment fragment);
    void inject(SelectOpponentFragment fragment);
    void inject(TeammatesInvitedFragment fragment);
    ...
}

Every new View that I add into my App (Fragment) needs to have its entry declared here. I was wondering if It's possible to generate this code automatically with some kind of annotation processor. The App has already several fragments, this component file has easily more than 300 entries. It'd be awesome if I could do something like:

@Injectable
public class MyNewFragment implements MyNewView {
...
}

And then automatically generate the entry in the ViewInjectorComponent file. It's possible? Where should I look at?

4gus71n
  • 3,717
  • 3
  • 39
  • 66

2 Answers2

1

The situation you are experiencing may be a consequence of organising your Modules and Components in an unusual way. In particular, grouping laterally (one Component injects all the Presenters) rather than vertically (one component injects the functionality related to SelectOpponentActivity) is problematic.

A good example to follow is in the Google Android Architecture Blueprints GitHub repo. If you peruse the code there, you will see that they have organised functionality related to Tasks inside one Java package together with a separate Component, Module, Presenter etc. This has the nice advantage of being able to restrict accessibility of the constructors of the classes contained therein and fulfilling Effective Java Item 13: Minimize the accesibility of classes and members.

Likewise, you've grouped all your modules together into a Presenter Module and an Interactor Module. The advice from the Dagger 2 official documentation is to organise Modules first for testability and then along functional lines. Again, you can refer to the Blueprint example for how to do this.

Finally, note that there is unavoidably some boilerplate involved in using most DI frameworks like Dagger 2. In a sense, you are exchanging a bigger problem ("how do I deal with all of these constructors?") with much smaller and more manageable problems ("how do I group my Components" etc.).

Update There is a library called Auto Dagger2 that can generate components for you. See this Github repo. Here is an example of an annotation:

@AutoComponent
@Singleton
public class ExampleApplication extends Application { 
}

Which generates the following code:

@Component
@Singleton
public interface ExampleApplicationComponent { 
}

Also check out Google Auto if you are interested in code generation tools.

David Rawson
  • 20,912
  • 7
  • 88
  • 124
  • That's nice, but if I'm injecting 500 views in my ViewComponent, following the rules described in that architecture I'll have 500 Component files instead of one single large file, right? It's the same issue but in a different way, lots and lots of duplicated code, that's what I'm trying to avoid. – 4gus71n Apr 03 '17 at 23:35
  • @4gus71n what classes are you exposing as injection targets inside your Components? In other words, what classes do you have the `void inject(Class clazz)` for? – David Rawson Apr 03 '17 at 23:55
  • Basically in all my Fragments. Activities in my App are nothing more than Fragment wrappers. In all my Fragments onCreate method I call MyApplication.getInstance().inject(this). – 4gus71n Apr 03 '17 at 23:58
  • @4gus71n when you're using a DI framework you are in a small way exchanging one type of problem for another. The DI framework makes it easier to test because it is easier to deal with constructors that pass in dependencies, but at the same time you have to write some boilerplate. This isn't really considered "code duplication" because it's not logic that needs to be tested and updated in multiple places when things change. – David Rawson Apr 04 '17 at 00:02
  • @4gus71n I updated my answer to include a link to a code gen tool. – David Rawson Apr 04 '17 at 02:10
  • Perfect that's exactly what I was searching! – 4gus71n Apr 04 '17 at 11:25
0

I'm not entirely sure that what I'm going to say is appropriate for an answer, but I'll take a chance here.

Even if you find a way to do what you want, don't do this in production (it is fine if you just want to learn code generation techniques).

The benefits you get from such an approach are small (not writing several lines of trivial code), but consider the drawbacks:

  1. The logic for code generation needs to be written/debugged/maintained
  2. Such an approach will be a violation of "principle of least astonishment"
  3. Code generation is BAD

Note the third point - I really mean it. Usage of code generation always leads to maintenance overhead, therefore it should be used only as a last resort.

Dagger by itself uses code generation, but for a good reason - performance. However, performance is not an issue in your case.

To summarize: your idea is very interesting, but approaches like this should not be used for production applications (unless this functionality is added to Dagger natively).

Vasiliy
  • 16,221
  • 11
  • 71
  • 127