5

I'm new to Dagger2 dependency injection. I have some difficulties to retain the same component when screen is rotated.

@Inject
MainActivityPresenterImpl presenter;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    component = DaggerMainActivityComponent.builder()
            .mainActivityModule(new MainActivityModule(this))
            .applicationComponent(TrainningDagger3.get(this).getComponent())
            .build();
    component.inject(this);
    presenter.fetchData();
}

I tried to debug the application and I think when new configuration is loaded its creating new instance of component and new instance of the presenter. How I can retain the same component and presenter when screen is rotated. Thank you so much!

Alexander
  • 333
  • 3
  • 17
  • You **have** to recreate the component since you add the Activity to it. `.mainActivityModule(new MainActivityModule(this))` will add the Activity and *leak* if you try to keep / store the component. I advise you to read some more tutorials on Dagger and familiarize yourself with different approaches that people take, but as a tl;dr there is no simple / easy way to keep Activity-scoped things during configuration change. – David Medenjak Dec 05 '17 at 11:48
  • Thanks David for your reply. I'm currently trying to save the state of the presenter with loaders, I'm not sure about the outcome :) . I'm not sure how I will be able to retain the same instance of MainActivityComponent when configuration change, since I have to initialize the MainActivityComponent inside MainActivity rather than in Application class because I need instance of MainActivity in module. Can you please suggest me some blog or some good explanation which will give me a little push on my back in my Dagger experience. Thanks – Alexander Dec 05 '17 at 13:36

2 Answers2

2

You're going to have to decide whether you want your Activity to be destroyed and recreated when the screen rotates (i.e. "the configuration changes"). If you want to handle it yourself, which I recommend, you simply make the change in your AndroidManifest.xml; if you want to handle it through Dagger, you're going to need to save your data in a longer-lived object (such as your ApplicationComponent).

As in the official docs on "Handling Configuration Changes", you can instruct your Activity to simply handle a method call instead of being restarted:

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

Now, when one of these configurations change, MyActivity does not restart. Instead, the MyActivity receives a call to onConfigurationChanged(). This method is passed a Configuration object that specifies the new device configuration. By reading fields in the Configuration, you can determine the new configuration and make appropriate changes by updating the resources used in your interface.

Importantly, this allows you to keep your ActivityComponent exactly as long-lived as your Activity itself, and can reason better about when your Activity's instance is valid or stale. You will still need to take care of loading and saving your data for Android's multitasking, but that is true no matter how you use Dagger.

If you think it is important to destroy and recreate your Activity on a configuration change, that's fine, but I would caution against using the term "activity component" if your are creating or modifying a component to live longer than a single Activity instance. That would be an atypical usage of the term, and might be very confusing for other developers. Instead, if you want a Dagger component to create and hold state for multiple Activity instances (of multiple activity classes, or multiple instances of the same activity), you should consider putting those objects into your existing ApplicationComponent, or creating a new component (e.g. "SessionComponent") that lives longer than one ActivityComponent but shorter than your ApplicationComponent. If you do this, you'll need to be very careful that nothing in this object holds onto an Activity instance, View, or anything that is permanently associated with a single Activity: That would cause a memory leak, as Android would not be able to garbage-collect those pieces of the Activity while your SessionComponent or ApplicationComponent holds a reference to it.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thank you for your reply Jeff its make sense now. I'm not still sure how I can initialize SessionComponent in Application class and still feed the MainActivityModule with MainAcivity instance. Hopefully I will found some example where module is depending on external dependencies and its created in Application class. Thank you – Alexander Dec 06 '17 at 11:05
  • Alexander, glad to help! As for how to initialize SessionComponent, you make it available to your application exactly as you make ApplicationComponent available, and you simply do not pass in the Application or Activity to it. The tricky part is, you'll need to figure out the best way to know when to use the same Session, and when to create a new one; the Bundle will help with that, but this is part of the reason I was recommending just handling the configuration change yourself. Cheers! – Jeff Bowman Dec 06 '17 at 18:20
0

Create a Application class and move you Daggger initialization to Application class's onCreate() method. By doing so your dagger initialization runs through out your application scope.

This issue you are facing is because your Activity gets destroyed and new activity is created if orientation is changed. So initializing dagger in Activity's onCreate method is not the proper way to do it.

And make sure you add @singleton in your dagger @provides method.

public class MainApplication extends Application {
@Override
public void onCreate() {
    super.onCreate();
    mainApplication = this;
    component = DaggerMainActivityComponent.builder()
            .mainActivityModule(new MainActivityModule(this))
            .applicationComponent(TrainningDagger3.get(this).getComponent())
            .build();
}

And dont forget to add android:name=".MainApplication" in you manifest's application tag.

Harish Rn
  • 98
  • 1
  • 9
  • Thank you for your help. I have two components, one is ApplicationComponent and the other one is MainActivityComponent. MainActivityComponent is depending on ApplicationComponent. I'm initializing ApplicationComponent in application class and MainActivityComponent in MainActivity. Do you think it's a good approach to initialize both components in Application class? Thank you! – Alexander Dec 03 '17 at 18:30
  • Using Activity wise components in every activity is not a good way. You can have a single Activity component for all you activites as a group. Have a look into this [link](https://www.youtube.com/watch?v=cx6pCIbOqtI&feature=youtu.be) and this [link](https://www.techyourchance.com/dagger-2-scopes-demystified/) and this [link](https://www.techyourchance.com/dependency-injection-android/). :) – Harish Rn Dec 03 '17 at 18:45
  • @Harish, I can't disagree more: The whole reason for having an Activity component separate from an Application component is to have objects that mirror the lifecycle of the Activity. Furthermore, due to the expense of classloading the component, it is ideal (if APK-size-heavy) to create a separate ActivityComponent for each Activity, so that you don't need to load up the graph for _every_ Activity in order to create _one_ Activity. See [dagger.android](https://google.github.io/dagger/android.html) for the official one-component-per-Activity framework. – Jeff Bowman Dec 05 '17 at 18:45
  • (You'll also see from that link to [dagger.android](https://google.github.io/dagger/android.html) that initializing dagger in your Activity's onCreate is _exactly_ the proper way to do it.) – Jeff Bowman Dec 05 '17 at 18:58