Your presenters don’t need all those lifecycle events
MVP is the new black in AndroidDev and there’s about a billion ways to do it.
One common mistake that is oft repeated is that developers keep including way too many activity/fragment lifecycle events inside their presenters ending up destroying the separation between the view and presentation layers. The benefits that MVP provides us — testing, agility and separation of concerns can only be achieved if we keep our presentation and UI layers decoupled.
My fellow developers, You must try your darndest to keep those presenters Android free.
So here’s what I propose
Let’s have only two lifecycle related methods inside your presenters. One will be called onViewAttached
and another will be it’s counterpart onViewDetached
.
Here’s what it looks like:
public interface Presenter {
void onViewAttached(MVPView view);
void onViewDetached();
}
All your presenters would implement this interface and in turn would have to implement these methods.
Great, but when do we call them from the view?
I prefer calling onViewAttached
and onViewDetached
during onStart
and onStop
respectively. This way your UI will truly be ready to be interacted with when onViewAttached
is called.
Here’s what that looks like:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
presenter.onViewAttached(this);
}@Override protected void onStop() {
super.onStop();
presenter.onViewDetached();
}
}
All your Activities would extend from this class and would get those method calls for free. Fragments can have a similar BaseFragment
class or something and extend from that.
What about saving and restoring state?
Here we have two options:
Let the view handle state management by itself: In this case your presenter doesn’t need any extra methods and the view will handle state restoration all on it’s own.
Manage state from the presenter : Here, you can have two extra methods in your BasePresenter
: onSaveState
and onRestoreState
which would be called by onSaveInstanceState
and onRestoreInstanceState
respectively.
You can pass the Bundle
object to these methods directly. This would break our “No Android in Presenters” rule but you can mock the Bundle
during tests using Mockito or the like without too much trouble.
Another way is to not use the bundle at all and save state somewhere else (DB, File, Shared prefs etc) but this might be slower to retrieve and you’ll have to clean up the saved state properly when you don’t need it anymore.
Go with whatever is more convenient to implement and test.
But I want to know if my Activity is new or not!
I hear ya. There are cases when you would need to know if your view is a new instance or an old one being re-created. This might be useful in situations where you need to do something only the first time a particular view instance is brought into the world.
To achieve this, we can modify our onViewAttached
method with an extra boolean parameter isNew
.
So our presenter interface becomes:
public interface Presenter {
void onViewAttached(MVPView view, boolean isNew);
void onViewDetached();
}
and our BaseActivity
becomes
public class BaseActivity extends AppCompatActivity {
private boolean isNewActivity;@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
// Check if it's a new view
isNewActivity = (savedInstanceState == null);
}
@Override protected void onStart() {
super.onStart();
presenter.onViewAttached(this, isNewActivity);
isNewActivity = false;
}
@Override protected void onStop() {
super.onStop();
presenter.onViewDetached();
}
}
We’re basically keeping track of a new instance creation in the Activity itself and passing this to the presenter whenever it’s attached.
I have a use case that needs more lifecycle events!
Great. Then add them in. I won’t claim that these 2 lifecycle events address every use case in the world and you might occasionally need more of them. When you do need them however, just make sure you’re being careful and not overdoing it.
Conclusion
MVP — good.
Too many lifecycle events in presenters — bad.
If you liked this, click the 👏 below. I notice each one and I’m grateful for every one of them.
For more musings about programming, follow me so you’ll get notified when I write new posts.