While iOS developers mostly use the Model–view–controller (MVC) design pattern and Windows Phone developers use the Model–view–viewmodel (MVVM) pattern while developing their apps, we, Android developers, are left with no hint from Google. You don’t really know where to start, which architecture fits your app’s business needs or which pattern would let your project be sustainable and maintainable for long time.
Well, let’s think of this positively! You have some space to innovate here. For me I choose the MVP (Model-View-Presenter) design pattern (with some variations) for most of the apps I work on independently.
In this repository you’ll find a demo app that implements the MVP pattern. We’ll discuss it later.
WHY MVP?
Well for me, 2 reasons mainly:
– Needless to say, absolute separation between layers.
– Unit testing: Whether you’re doing a TDD or planning to unit-test some components of your app, MVP should be your choice, really.
MVP LAYERS
The MVP pattern consists of 3 layers. Obviously: a Model, a View and a Presenter layer. Ideally, a Model (I like to call it a Service because a model tends to be associated with data objects for e.g: UserModel, CarModel.. etc) does some certain business logic or a network request or whatever processing needed to done on some data. A View is resposnble for displaying your data in a nice way (well, this is up to the UX team of course). And finally, a Presenter which acts as a messenger between the Model and the View.
THE DEMO APP
So the app has a BaseService, a BaseFragment and a BasePresenter, which respectivly represent the M, the V and the P.
The BasePresenter has a view of generic type which could be any fragment. A BaseFragment also has a presenter of generic type. And the BaseService has an instance of ServiceFinishedListener which is used to notify and pass a data object to the presenter whenever the business logic is done.
BasePresenter:
public abstract class BasePresenter<V extends BaseFragment> {
V view;
protected V getView() {
return view;
}
public void initView(V view) {
this.view = view;
}
}
BaseFragment:
public abstract class BaseFragment<P extends BasePresenter> extends Fragment {
protected P presenter;
View rootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(getLayoutId(), container, false);
if(presenter == null)
{
presenter = createPresenter();
presenter.initView(this);
}
return rootView;
}
protected abstract P createPresenter();
protected abstract int getLayoutId();
}
BaseService:
public abstract class BaseService {
protected ServiceFinishedListener serviceFinishedListener;
public BaseService(ServiceFinishedListener serviceFinishedListener) {
this.serviceFinishedListener = serviceFinishedListener;
}
public abstract void startService(Bundle b);
}
EXTENDING THESE GUYS
Now, to test this I made an IncrementService, IncrementPresenter, and InrcrementFragment who extend the base classes.
If a user presses on the Increment button in IncrementFragment (view) it wraps the parameters in a bundle, sends it to the presenter and tells the presenter to startTheIncrementingLogic(bundle).
presenter.startIncrementingLogic(b);
The IncrementPresenter (presenter) starts a new instance of the IncementService (model) and passes the bundle to it (It can call any number of services that do many llogic). It also subscribes for the ServiceFinishedListener to get notified when the service is done with its logic.
public void startIncrementingLogic(Bundle b) {
IncrementService incrementService = new IncrementService(new ServiceFinishedListener&amp;amp;lt;IncrementResult&amp;amp;gt;() {
@Override
public void onServiceFinished(IncrementResult result) {
}
});
incrementService.startService(b);
}
When the IncrementService is done (it basically adds 1 to some Integer(input), it calls the listener and passes the result object back to the presenter:
@Override
public void startService(Bundle b) {
int value = b.getInt(BundleConstants.VALUE_TO_INCREMENT);
value = doIncrementLogic(value);
IncrementResult incrementResult = new IncrementResult();
incrementResult.setValue(value);
serviceFinishedListener.onServiceFinished(incrementResult);
}
private int doIncrementLogic(int value) {
return value + 1;
}
And finally, a presenter would give an order to the view to set the value of a TextView. The IncrementFragment is dummy; it just follows the IncrementPresenter's order.
@Override
public void onServiceFinished(IncrementResult result) {
getView().setTextViewValue(result.getValue());
}
Here's a chart for the three components speaking to each other to get the job done (and yes, I suck big time in drawing):
Again, here's the demo app on Github: https://github.com/minafayek/MVPAndroidDemo
Happy Coding, for real!