I have a Mortar application, with a MortarActivityScope as the first child under the root scope. The MortarActivityScope has an ActivityScope which @Provides an activity for injected classes:
@Module(addsTo = ApplicationModule.class, injects = {Foo.class, SomePresenter.class, AnotherPresenter.class})
public class ActivityModule {
private final Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
@Provides Activity provideActivity() {
return activity;
}
}
public class Foo {
private final Activity activity;
@Inject(Activity activity) {
this.activity = activity;
}
public void doSomethingWithActivity() {
// do stuff with activity: findViewById(), getWindow(), mess with action bar etc.
}
}
This is fine until an orientation change happens. In the Mortar sample project, the Activity scope is not destroyed on orientation changes. This is presumably to allow @Singleton presenters, screens etc. to persist across orientation changes. You can see this in the onDestroy() method in the sample project's main activity:
@Override protected void onDestroy() {
super.onDestroy();
actionBarOwner.dropView(this);
// activityScope may be null in case isWrongInstance() returned true in onCreate()
if (isFinishing() && activityScope != null) {
MortarScope parentScope = Mortar.getScope(getApplication());
parentScope.destroyChild(activityScope);
activityScope = null;
}
}
}
However, doing it this way means that the old ObjectGraph persists across orientation changes. I have observed that Mortar.requireActivityScope
does not replace the module from the old activity scope with the new module provided by the new Blueprint. Instead, the object graph retains a reference to the previous module, including the destroyed Activity.
public class MyActivity extends Activity implements Blueprint {
@Inject foo;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MortarScope parentScope = Mortar.getScope(getApplication());
activityScope = Mortar.requireActivityScope(parentScope, this);
Mortar.inject(this, this);
foo.doSomethingWithActivity(); //fails, because activity injected by object graph is destroyed
}
@Override
public String getMortarScopeName() {
return getClass().getName();
}
@Override
public Object getDaggerModule() {
return new ActivityModule(this);
}
}
The Mortar sample activity seems to get around this by not including a @Provides Activity
method in the main module. But shouldn't the MortarActivityScope
be able to inject an Activity? What's the preferred way to do this, without losing all of your singleton objects (Presenter
objects, etc.) on orientation change?
Don't allow anyone to inject the Activity, that can't be made safe. Instead inject a presenter that is tied to the Activity.
How to handle onActivityResult() with Mortar includes an example of an Activity owning a presenter. Other parts of your app, including other presenters, can then inject that one and ask it to do whatever it is they need done that requires dealing with the activity.
And there is no need to tie all activity-specific work into a single activity presenter. Our activity has several presenters that broker its services to the rest of the app.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With