Planning on implementing MVP architecture for a MVC type android app. I have a concern on how I can make a presenter that will have multiple
models.
Typically a presenter's constructor will look like this:
MyPresenter(IView view, IInteractor model);
This way I can swap out dependencies when I'm testing and mock out view and model easily. But imagine my presenter is tied to an activity that must be multiple network calls. So for example I have one activity that does an API call for login and then another one for security questions, and then a third one for GetFriendsList. All those calls are in the same activity theme. How to do this with the constructor I showed above? or what is the best way to do this kind of thing? Or am I limited to having just one model and calling the services within that one model?
Presenter constructor need only the view.You don't need to dependent on the model. define your presenter and a view like that.
public interface Presenter{
void getFriendList(Model1 model);
void getFeature(Model2 model2);
public interface View{
void showFriendList(Model1 model);
void showFeature(Model2 model2)
}
}
now your implementation class has dependent on view part only.
rest your method will handle your model
class PresenterImpl implements Presenter{
View view;
PresenterImpl(View view){
this.view = view;
}
void getFriendList(Model1 model){
//Do your model work here
//update View
view.showFriendList(model);
}
void getFeature(Model2 model2) {
//Do your model work here
//updateView
view.showFeature(model2)
}
}
Related
This question is centered around the architecture of an Android Application. When using the LifeCycle component ViewModel, is it best to have one ViewModel per fragment or one ViewModel for the parent activity, to which the Fragments are subscribed to?
It seems unclear to me how to orient something like a Master-Detail fragment-activity relationship without some coupling. For instance, if each Fragment had it's own ViewModel, it is unclear how the Activity should know how to react without coupling (interface, direct functions calls).
As I mentioned in the comments, there is no unique way to accomplish this, but ideally, and very specifically to your Master/Detail flow concern, let's analyze the default provided example:
ItemDetialActivity handles fragment creation and display, FAB and menu actions. Note that there is nothing related to user data, only "system" handles . I, for instance, try to limit activities responsabilities to navigation, and stuff you really can't avoid like menu button handling. Now, ItemListActivity appears to be violating this principle because takes care of displaying the list (Google examples only create confusion -IMHO- between these separation of concerns), I would create a separate fragment that contains the RecyclerView and its adapter.
Now to the nitty gritty. Here is a very high-level skeleton I hope you can make use of. Check it out, implement it, and come back if there are any questions:
public interface BaseView {
LifecycleOwner lifecycleOwner();
/* perform actions that affect a basic screen status, like hide/show progress bars and errors,
animate views, etc. */
}
public class BaseRepo {
// will contain LiveData instances which will postValues()
}
public class FooRepo extends BaseRepo {
/* will contain access to database and networking functions, either by creating instance methods
or enforcing with an interface, it's up to you. */
}
public class BaseModel<P extends BasePresenter> extends ViewModel {
protected final FooRepo fooRepo; // optional, can be on concretes
<T> void subscribe(LiveData<T> liveData, Observer<T> observer) {
liveData.observe(view.lifecycleOwner(), observer);
}
<T> void unsubscribe(LiveData<T> liveData, Observer<T> observer) {
if (liveData != null) {
liveData.removeObserver(observer);
}
}
...
}
public abstract class BasePresenter<M extends BaseModel, V extends BaseView> implements LifecycleObserver {
protected V view;
protected M model;
public void setModel(M model) {
this.model = model;
}
public final void attachView(V view, Lifecycle lifecycle) {
this.view = view;
lifecycle.addObserver(this);
}
public void setPresenter(P presenter) {
this.presenter = presenter;
this.presenter.setModel(this);
}
...
}
public abstract class BaseFragment implements BaseView {
/* generics is highly encouraged here, I've seen examples of both BasePresenter<P>
and BaseView<P> */
protected P presenter;
/* You should bind layers here, or in the concrete class,
either with Dagger, reflection, or some other way */
#Override
public LifecycleOwner lifecycleOwner() {
return this;
}
...
}
Now, for every concrete screen you should create a presenter, model, and fragment that derive from the bases, and perform specifics there. I hope it helps.
I find myself where i need to play a sound file when user clicks a button on a view.
MediaPlayer requires a context to be created.
What is the best way to put MediaPlayer initialization code?
Should I pass a context into a presenter method and play it there?
Or is it ok to just play on the view.
Context is a part of Android View Layer in MVP, so Presenter must not have any idea about it and you should not pass it to presenter.
You have to add a methods to your View interface and implement it inside your android view components (i.e. Activity or Fragment) and use them to do an action in View layer as playing a sound.
Presenter must ask for the UI event and View must handle it!
Here is a MVP sample using Dagger, RxJava and Retrofit, which might help you to learn more about MVP in Android:
https://github.com/mmirhoseini/marvel
I often put business logic code in Model Layer (don't make confusion with model in database). I often rename as XManager for avoiding confusion (such as ProductManager, MediaManager ...) so presenter class just uses for keeping workflow.
The rule of thumb is no or at least limit import android package in presenter class. This best practice supports you easier in testing presenter class because presenter now is just a plain java class, so we don't need android framework for testing those things.
For example here is my mvp workflow.
View class: This is a place you store all your view such as button, textview ... and you set all listeners for those view components on this layer. Also on this View, you define a Listener class for presenter implements later. Your view components will call methods on this listener class.
class ViewImpl implements View {
Button playButton;
ViewListener listener;
public ViewImpl(ViewListener listener) {
// find all view
this.listener = listener;
playButton.setOnClickListener(new View.OnClickListener() {
listener.playSong();
});
}
public interface ViewListener {
playSong();
}
}
Presenter class: This is where you store view and model inside for calling later. Also presenter class will implement ViewListener interface has defined above. Main point of presenter is control logic workflow.
class PresenterImpl extends Presenter implements ViewListener {
private View view;
private MediaManager mediaManager;
public PresenterImpl(View, MediaManager manager) {
this.view = view;
this.manager = manager;
}
#Override
public void playSong() {
mediaManager.playMedia();
}
}
Manager class: Here is the core business logic code. Maybe one presenter will have many managers (depend on how complicate the view is). Often we get Context class through some injection framework such as Dagger.
Class MediaManagerImpl extends MediaManager {
// using Dagger for injection context if you want
#Inject
private Context context;
private MediaPlayer mediaPlayer;
// dagger solution
public MediaPlayerManagerImpl() {
this.mediaPlayer = new MediaPlayer(context);
}
// no dagger solution
public MediaPlayerManagerImpl(Context context) {
this.context = context;
this.mediaPlayer = new MediaPlayer(context);
}
public void playMedia() {
mediaPlayer.play();
}
public void stopMedia() {
mediaPlayer.stop();
}
}
Finally: Put those thing together in Activities, Fragments ... Here is the place you initialize view, manager and assign all to presenter.
public class MyActivity extends Activity {
Presenter presenter;
#Override
public void onCreate() {
super.onCreate();
IView view = new ViewImpl();
MediaManager manager = new MediaManagerImpl(this.getApplicationContext());
// or this. if you use Dagger
MediaManager manager = new MediaManagerImpl();
presenter = new PresenterImpl(view, manager);
}
#Override
public void onStop() {
super.onStop();
presenter.onStop();
}
}
You see that each presenter, model, view is wrapped by one interface. Those components will called through interface. This design will make your code more robust and easier for modifying later.
This is such a long post for answering your question. I think this is suitable because everyone has their own MVP implementation (core flow is same, but minorities are different). So I present here a workflow I often use in work. Hoping you see this useful :)
If you need a generic Context you can extend Application, declare a static context variable and after you can get this Context into your presenter.
I have just started using MVP in android development. As per different tutorials found on web, I am creating 5(viewInterface, presenterInterface, presenterImpl, interector, and interectorImpl) files for each fragment. Is there any way by which i can reduce the number of these files.
Thanks
As you have come to know basics of Clean Architechure. The following example depicts how actual your MVP pattern is implemented.
Example:
interface BaseContract {
interface BaseView {
//Methods for View
void onDoSomething();
}
interface BasePresenter {
void doSomething();
}
}
class BaseMainPresenter implements BaseContract.BasePresenter {
BaseContract.BaseView view;
BaseMainPresenter(BaseContract.BaseView view) {
this.view = view;
}
#Override
public void doSomething() {
if (view != null)
view.onDoSomething();
}
}
class DemoClass implements BaseContract.BaseView {
//Create object of Presenter
/****
* Example :
* BaseMainPresenter baseMainPresenter = new BaseMainPresenter(this);
*/
#Override
public void onDoSomething() {
//Deal with Context here.
}
}
You can refer as MVP Pattern with this following link :-
Link : https://github.com/Archish27/Retrozing
Yeah- don't do whatever it is you're doing. MVP is a good pattern for some usecases, but there's never a reason for more than 3 classes- a model, a view, and a presenter. Any tutorial that is going into that depth is being way over formal and is actually a harmful tutorial.
Android is basically MVP by default. Your Presenter is your Activity class. Your View is your layout- that hierarchy of View classes. Your model is whatever data you need to run your app.
Don't get hung up on overly formal explanations of patterns, and don't apply patterns to apply a pattern. The real use of patterns isn't a goal to make your application look like that, the real use of a pattern is a way to describe what you're doing to other architects/programmers. You should never look at a pattern and try to find ways to use it or look at a problem and think "what patterns can I use". You should see a problem, think of a solution, and that may be a pattern. By the time you're ready to study patterns, you should have seen the vast majority of them in code already.
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?
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.
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.
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();
}
}
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)
}
I started to study MVP as I read about it is a very good pattern for developing Android apps.
I have a class (Model) for example:
public class Level {
int difficulty;
int enemies;
public int getXpReward() {
return enemies * difficulty;
}
}
And I have another class (View) for example:
public class LevelView extends FrameLayout {
TextView xpRewardTextView;
/*ctr and view init......codehere*/
public updateOptionOne(Level level) {
xpRewardTextView.setText(Integer.toString(level.getXpReward()));
}
public updateOptionTwo(int xpReward) {
xpRewardTextView.setText(Integer.toString(xpReward));
}
}
Should I call the optionOne function from my Presenter so passing a whole instance of Level?
Like:
myPresenter.updateView(MyDb.getLevel())
Or
Should I call the optionTwo function from my Presenter so only passing the very data we need to the View, like only JAVA type attributes
Like:
myPresenter.updateView(MyDb.getLevel().getXpReward())
So basically my question is do Views can have Model types as arguments of their update functions? Because I read an MVP principle about the Views should not know anything about Models.
In this case yes keep the View simple as possible.
For larger and more complex application you sometime need classes to communicate between the view and the presenter. In that case you should have a Model for the View layer and one for the data layer and convert them at the presentation layer.