9

Assuming what have been said here, it's the developer's responsibility to keep the component instance in order to implement their own scope logic (since a scoped method will return the same instance for a given component).

What's the clean way to keep this component reference through the activity lifecycle ?

Example : You're implementing the MVP pattern, so you need a Presenter within your Activity. This Presenter can do a network operation to download items. When the device rotate, your Activity is being destroyed and recreated but you would like to keep the network operation going and just get back the pre-rotation presenter.

Scoping the Component providing the Presenter with a custom PerActivity scope is the solution, so you have to keep the Component instance through this rotation to get injected the same instance of Presenter as the first launch of the Activity.

How can we deal with this ? I thought of a kind of Component Cache (like a HashMap ?) that could be provided by an Application Component living inside the Application class.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
Rafi Panoyan
  • 822
  • 9
  • 21
  • 2
    Your options are: - component cache in application; - custom non-configuration instance; - retained fragment. – EpicPandaForce Aug 04 '16 at 13:07
  • **Personally** I just block event dispatch through the event bus while the current activity isn't `resume`d, and put the presenter state in a bundle, then restore its state before events are resumed. And my presenters are therefore unscoped. – EpicPandaForce Aug 04 '16 at 13:08
  • How can you save the presenter state in a bundle if this state if for example an OkHttp client downloading data ? In this case a presenter cache seems required. – Rafi Panoyan Aug 04 '16 at 16:24
  • 1
    I think the short answer is that if the logic that does your network operation needs to live longer than the `Activity` instance, it _isn't activity scoped_. Can you just move that logic into a wider scope? – gk5885 Aug 04 '16 at 17:40
  • As I said, frozen event dispatching through the event bus. That's just what I do, anyways. If I really wanted to preserve it, I'd probably use the retained fragment approach. – EpicPandaForce Aug 04 '16 at 17:53

2 Answers2

0

You can see the implementation of ribot/android-boilerplate showcase app. The solution they chose is to have a static Map<Long, ConfigPersistentComponent> inside the BaseActivity, from which all activities extend from.

public class BaseActivity extends AppCompatActivity {

    private static final AtomicLong NEXT_ID = new AtomicLong(0);
    private static final Map<Long, ConfigPersistentComponent> sComponentsMap = new HashMap<>();

    private ActivityComponent mActivityComponent;
    private long mActivityId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create the ActivityComponent and reuses cached ConfigPersistentComponent if this is
        // being called after a configuration change.
        mActivityId = savedInstanceState != null ?
                savedInstanceState.getLong(KEY_ACTIVITY_ID) : NEXT_ID.getAndIncrement();

        ConfigPersistentComponent configPersistentComponent;
        if (!sComponentsMap.containsKey(mActivityId)) {
            // Creating new component
            configPersistentComponent = DaggerConfigPersistentComponent.builder()
                    .applicationComponent(BoilerplateApplication.get(this).getComponent())
                    .build();
            sComponentsMap.put(mActivityId, configPersistentComponent);
        } else {
            // Reusing component
            configPersistentComponent = sComponentsMap.get(mActivityId);
        }
        mActivityComponent = configPersistentComponent.activityComponent(new ActivityModule(this));
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putLong(KEY_ACTIVITY_ID, mActivityId);
    }

    @Override
    protected void onDestroy() {
        if (!isChangingConfigurations()) {
            // Activity is finishing, removing the component
            sComponentsMap.remove(mActivityId);
        }
        super.onDestroy();
    }

    ...

}
azizbekian
  • 60,783
  • 13
  • 169
  • 249
0

Network can work with app context. This is how i would design Applicationcomponent with appscope Now i would have this created on application layer ApplicationComponent Shouod take context as external dependency

Next is activitycomponent extending on activitymodule with peractivityscope ..depending on applicationcomponet

In my every activity i would create activityComponet by providing it applicationcomponet This applicationcomponet can be accessed via. Activity.getapplication().getapplicationcomponent()

Here make sure your applicationmodule providing network method has appscope If thats the case u should get same network even on app rorate.

Look for GitHubapplication sample will post the link in next edit.

Also it will he worth while to look at livedata (out of context for this question)

Jileshl
  • 146
  • 6