Android MVP open Activity from Presenter, anti-pattern?

12,627

Solution 1

Yes it's an anti-mvp-pattern. Based on passive view in MVP, you lost your testability, because you don't have to deal with the android framework in your presenter.

So it's better to manage the navigation of the app from the View Layer.

class MyPresenter {
    MyPresenter.View view;

    void backButtonClicked() {
        view.navigateToHomeScreen();
    }

    public interface View {
        void navigateToHomeScreen();
    }
}

class MyActivity extends Activity implements MyPresenter.View {
    @Override
    void navigateToHomeScreen() {
        startActivity(...)
    }

    @OnClick(R.id.my_button)
    void onClick() {
        presenter.backButtonClicked();
    }
} 

Also another advantage of this way is that it will be easy to replace activity with a fragment or a view.

Edit 1:

Morgwai said this way will break separation of concern and single responsibility, but you cannot have single responsibility every where. Sometime you need to violate it. Here is an example from Google for MVP:

TaskDetailPresenter calls ShowEditTask which is responsible to open a new Activity inside TaskDetailFragment.

But also you can use CommandPattern which is a better approach

interface NavigationCommand {
    void navigate();
}

So, Presenter will use it when it needs.

Solution 2

As I wrote in my comment to the accepted answer, I think that managing navigation from the view layer is a clear breaking of separation of concerns rule: views should contain ONLY methods to update current UI screen.

The problem originates from the android platform design as Activity and Fragment classes contain both methods to operate on UI screen and to send intent objects that start other activities like startActivity.

A clean way to solve this would be to create some Navigator interface that would contain methods related to navigation, make activities implement it and inject it into presenters as well. This way at least from the presenters' standpoint navigation and UI manipulation would be separated. It may however look odd from activities' standpoint: now they would often implement both interfaces (Navigator and View) and pass their reference 2 times to the presenter. If because of this reason you decide to manage navigation from your view layer then at least keep methods for navigating separate from those for manipulating UI: never perform navigation and UI manipulation in the same method.

Solution 3

In my opinion it would be better if you open an activity from the View Layer. I prefer that Presenter knows about Activity as little as possible.

If there is some condition of what activity should be started, you can use something like this:

public class Presenter {

    private ViewsPresentation mViewsPresentation;

    public void someButtonClicked() {
        if (/*some condition*/) {
            mViewsPresentation.startFirstActivity();
        } else {
            mViewsPresentation.startSecondActivity();
        }
    }

    public interface ViewsPresentation {
        void startFirstActivity();
        void startSecondActivity();
    }

}

Solution 4

I have made this solution (in Kotlin):
I created an Interface called ViewNavigator

interface ViewNavigator {
    fun navigateTo(target: Class<*>)
}

Then I made the View Interface Implement it

interface View : ViewNavigator {
    //...
}

Then the Actual View (the activity) can override the navigateTo function

override fun navigateTo(target: Class<*>) {
    startActivity(Intent(this, target))
}

So, whenever I want to navigate to any activity, I can simply write that in the presenter class. For example:

override fun onAnimationFinished() {
    view.navigateTo(HomeActivity::class.java)
}
Share:
12,627
Jose M Lechon
Author by

Jose M Lechon

Android Mobile Developer, however I'm opened to whatever language.

Updated on June 04, 2022

Comments

  • Jose M Lechon
    Jose M Lechon about 2 years

    Would it be an anti-pattern if from a Presenter layer I open an Activity?

    If so, should I manage the navigation of the app from the View Layer?