5

I have an Android Activity that I'm using Dagger2 to inject a Presenter into. I'd like my Presenter to be capable of holding state even if a configuration change occurs.

For instance, I'm going to use the Presenter to kick off a network call and if the user rotates the device while the network call is in-flight I'd like to be able to receive the response after the device finishes its rotation and not have to restart the call.

I'm getting tripped up because if I scope the instance of Presenter to the Activity's life, then isn't there a chance that the Presenter would be garbage collected when the Activity goes through onDestroy() during a configuration change? My other thought was to use a scope that is valid during the life of the application. However, if I do that how do I ensure that my Presenter can be garbage collected once the Activity has been destroyed for good (not due to a config. change, but something like the back button being pressed)?

Is there a way to ensure that my Presenter will survive an Activity's configuration change and also not be leaked for the life of the Application?

neonDion
  • 2,278
  • 2
  • 20
  • 39

2 Answers2

5

I would strongly advice against trying to implement this approach.

You're effectively trying to use DI framework in order to support Activity specific life-cycle flow, although DI frameworks are not intended to be used like this.

I recently answered another similar question in which OP tried to share state in View-Model between different Activities. Although use cases are not identical, the general pattern is the same - attempt to delegate flow control responsibilities to DI framework, which is not a good idea.

The best approach in your case (IMHO) would be to store the current state before rotation, re-instantiate the presenter upon rotation, and then restore its state.

How you store the state during rotation depends on what exactly you're trying to preserve:

  • If you need to preserve UI related state (selections, texts, position of elements, etc.) then you can use the usual onSaveInstanceState() and onRestoreInstanceState() callbacks
  • If you need to preserve some business related state (ongoing network requests, data, data modifications, etc.) then encapsulate this logic in a business class (e.g. SomeBusinessUseCaseManager) and inject this class from Application wide component with a scope.

You can find a detailed review of Dagger's scopes here.

More information about DI in Android can be found here.

Community
  • 1
  • 1
Vasiliy
  • 16,221
  • 11
  • 71
  • 127
  • Your second recommendation is similar to what I'm doing with my Presenter. Presenter has several UseCases injected into it (I'm creating instances of UseCase in Activity and injecting in Presenter using constructor). If I move UseCase instantiation to the component that lives for the life of the application then the UseCase could continue to do work during a config. change and when a new Presenter (scoped to activity) is created after config. change it will get the already running use cases injected. That solves the issue of needing to redo something that was already started. – neonDion Jan 26 '17 at 17:38
  • @neonDion, sounds like a solid plan. Please note that if you do this, you'll get additional benefit of getting out of presenters and abstracting network related stuff. This is a big step towards decoupled design. – Vasiliy Jan 26 '17 at 17:49
0

According to this article about Custom Scopes:

http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

In short - scopes give us “local singletons” which live as long as scope itself.

Just to be clear - there are no @ActivityScope or @ApplicationScope annotations provided by default in Dagger 2. It’s just most common usage of custom scopes. Only @Singleton scope is available by default (provided by Java itself), and the point is using a scope is not enough(!) and you have to take care of component that contains that scope. This mean keeping a reference to it inside Application class and reuse it when Activity changes.

public class GithubClientApplication extends Application {

    private AppComponent appComponent;
    private UserComponent userComponent;

    //...

    public UserComponent createUserComponent(User user) {
        userComponent = appComponent.plus(new UserModule(user));
        return userComponent;
    }

    public void releaseUserComponent() {
        userComponent = null;
    }

    //...
}

You can take a look at this sample project:

http://github.com/mmirhoseini/marvel

and this article:

https://hackernoon.com/yet-another-mvp-article-part-1-lets-get-to-know-the-project-d3fd553b3e21

to get more familiar with MVP and learn how dagger scope works.

Mohsen Mirhoseini
  • 8,454
  • 5
  • 34
  • 59
  • You have a lot of information in your links. Can you add a brief synopsis in your answer that answers the question: Is there a way to ensure that my Presenter will survive an Activity's configuration change and also not be leaked for the life of the Application? – neonDion Jan 25 '17 at 22:42
  • Okay, then I will edit the answer, but at least take a look at this one cause it is necessary to know that scopes are just a tag and unfortunately they don't do anything and you have to handle everything: http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/ – Mohsen Mirhoseini Jan 26 '17 at 05:48