Arman Chatikyan

@chatikyan

MVP + Android Architecture Components = ❤

MVP + Android Architecture Components = ❤

Few days ago, during Google I/O was presented Android Architecture Components. We decided to combine these components with MPV Architecture. So, let’s see what we got in a result.

One of the common problems working with MPV Architecture is to keep Presenter’s state while changing screen orientation. There are some solutions for that (Loaders, retainInstance, Mosby, Moxy and so on).

One of those Architecture Components presented during the I/O was a ViewModel.

The ViewModel class is designed to store and manage UI-related data so that the data survives configuration changes such as screen rotations.” — from official documentation.

It gives us an opportunity to save the object during screen orientation change.

The other component is Lifecycle, which we will use.

The android.arch.lifecycle package provides classes and interfaces that let you build lifecycle-aware components — which are components that can automatically adjust their behavior based on the current lifecycle of an activity or fragment.” - from official documentation.

1. Before orientation change

2. After orientation change

From the beginning let’s design our Presenter and View. Let’s create a Contract Interface.

public interface BaseContract {

interface View {

}

interface Presenter<V extends BaseContract.View> {

void attachLifecycle(Lifecycle lifecycle);

void detachLifecycle(Lifecycle lifecycle);

void attachView(V view);

void detachView();

V getView();

boolean isViewAttached();

void onPresenterDestroy();
}
}

After this, we need to create Presenter that implements our Presenter Contract.

public abstract class BasePresenter<V extends BaseContract.View> implements LifecycleObserver, BaseContract.Presenter<V> {

private V view;

@Override
final public V getView() {
return view;
}

@Override
final public void attachLifecycle(Lifecycle lifecycle) {
lifecycle.addObserver(this);
}

@Override
final public void detachLifecycle(Lifecycle lifecycle) {
lifecycle.removeObserver(this);
}

@Override
final public void attachView(V view) {
this.view = view;
}

@Override
final public void detachView() {
view = null;
}

@Override
final public boolean isViewAttached() {
return view != null;
}
}

So, we have a Presenter, which attaches and detaches the View and Lifecycle Observer.

With the next step, let’s create our Activity. Here we have a problem: the presenter will be recreating every time after the orientation change.

Let’s go ahead.

The ViewModel is automatically retained during configuration changes so the data it holds is immediately available to the next activity or fragment instance. This will help us not to initialize the Presenter every time.

For that, we will create the Class, which extends from ViewModel. It will receive and return our presenter’s object. ViewModel also has onCleared() method. If the activity is re-created, it receives the same ViewModel instance that was created by the previous activity. When the owner activity is finished, the Framework calls ViewModel’s onCleared() method so that it can clean up resources.

public final class BaseViewModel<V extends BaseContract.View, P extends BaseContract.Presenter<V>> extends ViewModel {

private P presenter;

void setPresenter(P presenter) {
if (this.presenter == null) {
this.presenter = presenter;
}
}

P getPresenter() {
return this.presenter;
}

@Override
protected void onCleared() {
super.onCleared();
presenter.onPresenterDestroy();
presenter = null;
}
}

In our next step we will create the BaseViewModel instance inside the Activity.

Also we must give the Lifecycle to the Presenter. So we need LifecycleRegistry. And the Activity must implement to the LifecycleRegistryOwner.

public abstract class BaseActivity<V extends BaseContract.View, P extends BaseContract.Presenter<V>> extends AppCompatActivity implements BaseContract.View, LifecycleRegistryOwner {

private final LifecycleRegistry lifecycleRegistry =
new LifecycleRegistry(this);
protected P presenter;

@CallSuper
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BaseViewModel<V, P> viewModel =
ViewModelProviders.of(this).get(BaseViewModel.class);
if (viewModel.getPresenter() == null) {
viewModel.setPresenter(initPresenter());
}
presenter = viewModel.getPresenter();
presenter.attachLifecycle(getLifecycle());
presenter.attachView((V) this);
}

@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}

@CallSuper
@Override
protected void onDestroy() {
super.onDestroy();
presenter.detachLifecycle(getLifecycle());
presenter.detachView();
}

protected abstract P initPresenter();
}

At the first time creating the Activity, we must create a Presenter and give it to the ViewModel. The next time when the activity recreates, we just reuse our Presenter from the ViewModel.

At the first time creating Presenter, we must attach Lifecycle and View. For avoiding memory leak, we also need to detach Lifecycle and View in onDestroy().

As a bonus, we also have Activity Lifecycle events inside presenter.

@OnLifecycleEvent(value = Lifecycle.Event.ON_CREATE)
protected void onCreate() {

}

For the complete puzzle, something lacks. It is a state of Views.

For that, we must have Bundle getStateBundle() in our BaseContract, and Bundle stateBundle in the Presenter, which will keep and return our View state.

The final Presenters is like that:

public abstract class BasePresenter<V extends BaseContract.View> implements LifecycleObserver, BaseContract.Presenter<V> {

private Bundle stateBundle;
private V view;

@Override
final public V getView() {
return view;
}

@Override
final public void attachLifecycle(Lifecycle lifecycle) {
lifecycle.addObserver(this);
}

@Override
final public void detachLifecycle(Lifecycle lifecycle) {
lifecycle.removeObserver(this);
}

@Override
final public void attachView(V view) {
this.view = view;
}

@Override
final public void detachView() {
view = null;
}

@Override
final public boolean isViewAttached() {
return view != null;
}

@Override
final public Bundle getStateBundle() {
return stateBundle == null ?
stateBundle = new Bundle() : stateBundle;
}

@CallSuper
@Override
public void onPresenterDestroy() {
if (stateBundle != null && !stateBundle.isEmpty()) {
stateBundle.clear();
}
}
}

Thanks for reading this article. You can find full code there.

Let’s become friends on Twitter, Github and Facebook. If you enjoyed the writings then please use the ❤ heart below to recommend this article so that others can see it.

We will also love to hear your comments and suggestions :)

Thanks.

More by Arman Chatikyan

Topics of interest

More Related Stories