Android MVP — An Alternate Approach

Originally posted March 11, 2015

Edit: July 19, 2016: I now consider this article “deprecated” as I have since joined the masses in treating Activities and Fragments as views, with necessary system services injected into separate presenters.

Edit: March 9, 2017: My current ideal MVP architecture for Android is reflected in this sample that is included in my view-state-navigation library Okuki. You can read more about Okuki here.

Today I want to share my own approach to implementing the MVP (Model-View-Presenter) pattern in Android. If you are unfamiliar with the MVP pattern or why you would want to use it in your Android application, I recommend first reading this Wikipedia article and this blog post.

Activities and Fragments as Views?

The leading consensus around MVP implementation in Android (as described in the blog post linked above) is that Activities and Fragments should be treated as Views. In these implementations Presenters are generic objects that are instatiated or injected in their respective Activity/Fragment-Views. I agree that having your Presenters completely free of any android.* imports makes writing test cases on them very easy (via plain JUnit, no instrumentation required). I also agree that by having your Presenters separate from the Activity lifecycle, retaining state across configuration changes is easier. On the other hand, however, Activities have very complex lifecycles and Fragments even more so. And various activity/fragment lifecycle events are likely to be very important to your business logic. Additionally, Activities have access to your Context and various Android system services… Activities send Intents, start Services, create and execute FragmentTransisitons, etc. All of these complexities are, in my opinion, outside of the scope of what a “View” should be concerned with. A View’s job is to present data and get input from the user. Ideally, a View should be devoid of business logic, making a unit test of the View unnecessary. Given this, I wasn’t satisifed with this consensus approach, and set about trying to implement Activities and Fragments and Presenters.

Activities and Fragments as Presenters

Step 1: Getting rid of all the view-ey stuff

The biggest challenge in converting Activities and Fragments into Presenters was to separate out all of the UI logic. I broadly defined a UI element as anything in the android.view or android.widget packages, and then proceeded to try to separate them from my Presenters in such a way that would be consistent, regardless of the type of presenter. The solution I came up with was to create a layer of abstraction around the View. The result is the interface below, which for lack of another word I called Vu:

public interface Vu {  
void init(LayoutInflater inflater, ViewGroup container);
View getView();
}

As you can see, Vu defines a generic initialization routine into which I can pass an inflator and a container view. It also contains a method for getting an actual View instance. Each presenter will be associated with its own Vu which will implement this interface (either directly, or indirectly by implemented an interface that extends from Vu).

Step 2: Creating the Base Presenters

Now that I have a basis for View abstraction, I can set about defining a number of “Base” Activity/Fragment classes which make use of Vu for View instatiation. The way I accomplished this was by making use of generic types and an abstract method for specifying a particular Presenter’s Vu class. This is the most tedious part of the implementation, because I need to re-implement similar logic or each type of base Presenter class that I want to use.

Here is how I implement this with android.app.Activity:

public abstract class BasePresenterActivity<V extends Vu> extends Activity {

protected V vu;

@Override
protected final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
vu = getVuClass().newInstance();
vu.init(getLayoutInflater(), null);
setContentView(vu.getView());
onBindVu();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

@Override
protected final void onDestroy() {
onDestroyVu();
vu = null;
super.onDestroy();
}

protected abstract Class<V> getVuClass();

protected void onBindVu(){};

protected void onDestroyVu() {};

}

And here’s the same implementation with android.app.Fragment:

public abstract class BasePresenterFragment<V extends Vu> extends Fragment {

protected V vu;

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

@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = null;
try {
vu = getVuClass().newInstance();
vu.init(inflater, container);
onBindVu();
view = vu.getView();
} catch (java.lang.InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return view;
}

@Override
public final void onDestroyView() {
onDestroyVu();
vu = null;
super.onDestroyView();
}

protected void onDestroyVu() {};

protected void onBindVu(){};

protected abstract Class<V> getVuClass();

}

The same logic can be repeated for other Activity and Frament types, such as those from the support libraries, etc.

As you can see, I am overriding and finalizing the methods which instantiate the views (onCreate and onCreateView) and those ones that tear them down (onDestroy and onDestroyView). I chose to finalize these methods to enforce the use of the abstracted Vu instances. Since they are finalized, I created new lifecycle methods that each implementation can use for initialization and teardown: onBindVu and onDestroyVu. A benefit of this approach is that implementations of both types of presenter make use of the same signatures for these lifecycle events. This smooths out the differeces between Activity and Fragment lifecycles, and makes converting from one type of Presenter to another much easier. (You’ll also notice that I’m not really doing anything with InstantiationException or IllegalAccessException. This just me being lazy, because if I use these classes correctly these exceptions will never be thrown.)

Step 4: Putting it to work

Now that we’ve got our framework in place, we can start using it. For the sake of keeping it short, I’ll show a “Hello World” example. I’ll start by creating a new class that implements the Vu interface:

public class HelloVu implements Vu {

View view;
TextView helloView;

@Override
public void init(LayoutInflater inflater, ViewGroup container) {
view = inflater.inflate(R.layout.hello, container, false);
helloView = (TextView) view.findViewById(R.id.hello);
}

@Override
public View getView() {
return view;
}

public void setHelloMessage(String msg){
helloView.setText(msg);
}

}

Next, I’ll create the Presenter that will control this view:

public class HelloActivity extends BasePresenterActivity<HelloVu> {

@Override
protected void onBindVu() {
vu.setHelloMessage("Hello World!");
}

@Override
protected Class<MainVu> getVuClass() {
return HelloVu.class;
}

}

Hold on… coupling alert!

You’ll notice that my HelloVu class implements Vu directly, and that my Presenter’s getVuClass() references the implementation directly. Conventionally in the MVP pattern, Presenters are decoupled from their Views via an interface. Well, you can certainly do this. Instead of implementing Vu directly, we could make an interface IHelloView that extends Vu, and use this interface for the Type defined by the presenter. The Presenter then becomes something like this:

public class HelloActivity extends BasePresenterActivity<IHelloVu> {

@Override
protected void onBindVu() {
vu.setHelloMessage("Hello World!");
}

@Override
protected Class<MainVu> getVuClass() {
return HelloVuImpl.class;
}

}

I personally don’t see much benefit in abstracting my Vu implementation behind an interface, esp. with powerful mocking tools at my disposal. But a nice aspect of my approach is that it works with or without specific Vu interfaces. The only requirements is that you ultimately implement Vu.

Step 5: Testing

Okay, if I was really following TDD this should really be Step 1. But that aside, you can see the Activity has become very concise, and with all of the UI logic removed, it becomes easy to test. Here is a unit test:

public class HelloActivityTest {

HelloActivity activity;
HelloVu vu;

@Before
public void setup() throws Exception {
activity = new HelloActivity();
vu = Mockito.mock(HelloVu.class);
activity.vu = vu;
}

@Test
public void testOnBindVu(){
activity.onBindVu();
verify(vu).setHelloMessage("Hello World!");
}

}

This is just a standard JUnit test, so it can be run without any Android instrumentation. Of course the Activity we are testing is about as simple as it can get. In practice you are likely to need to test something that requires at least a minimal amount of instrumentation support. For example, you may need to test the code that runs in the onResume() method. Doing so without any instrumentation support you’ll get a Exception as soon as you hit super.onResume(). Luckily there are tools like Robolectric, and the testOptions { unitTests.returnDefaultValues = true } option on new the built-in Unit Testing support in the Android Gradle 1.1 plugin and Android Studio. Aside from that, however, you can also expand your Base Presenter classes to abstract out these lifecycle events like this:

...

@Override
protected final void onResume() {
super.onResume();
afterResume();
}

protected void afterResume(){}

...

Now you can move your application specific logic to your new lifecycle events and run your tests completely instrumentation-free.

Bonus: Adapters as Presenters

As tricky as Activities are in separating View from Presenter, adapters can be even more confusing. Are they Views or are they Presenters? Well, my opinion should be obvious by now. So less talk, and more code:

public abstract class BasePresenterAdapter<V extends Vu> extends BaseAdapter {

protected V vu;

@Override
public final View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
try {
vu = (V) getVuClass().newInstance();
vu.init(inflater, parent);
convertView = vu.getView();
convertView.setTag(vu);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
vu = (V) convertView.getTag();
}
if(convertView!=null) {
onBindListItemVu(position);
}
return convertView;
}

protected abstract void onBindListItemVu(int position);

protected abstract Class<V> getVuClass();

}

As you can see, the implementation is almost the same as with Activity and Fragment Presenters. However, instead of an “empty” onBindVu method, I have a onBindListItemVu method that takes int position as input. Also, I went ahead and baked-in the View Holder pattern.

Conclusion and Demo Project

What I’ve described in this article has been my own approach to implementing MVP. From what I have found researching the interwebs it seems to be a unique approach. I am very curious to hear feedback from other Android developers on this. Is anyone else taking this approach? Do you find it useful? Am I just crazy? And if I am crazy, is it in a good way?

I have been working on putting this technique (along with some other goodies such as Dagger integration) together into an open-source library/framework that I hope to release soon. But in the meantime, I have a demo project available at https://github.com/wongcain/MVP-Simple-Demo. Check it out and let me know your thoughts.


The original Disqus thread for this article can be found here.