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.
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 have been learning and integrating MVP pattern, and have few questions.
What i have understand from this diagram, is that
Activity will create instance of Presenter, and pass its reference and model object to the presenter
MainPresenter mainPresenter = new MainPresenter(this, new MainModel());
Next if presenter need to store or get any data from local preference or remotely, it will ask model.
And then model will ask repository for storing and retrieving data.
I followed few tutorials and this is how i implement the pattern.
Interface
public interface MainActivityMVP {
public interface Model{
}
public interface View{
boolean isPnTokenRegistered();
}
public interface Presenter{
}
}
Activity
MainPresenter mainPresenter = new MainPresenter(this, new MainModel());
mainPresenter.sendDataToServer();
Presenter
public void sendDataToServer() {
// Here i need to ask `model` to check
do network operation and save data in preference
}
Now the issue is i need context to access sharedPreference, but i have not passed context anywhere. I also don't want to use static context. I want to know the proper way of passing context to MVP pattern.
Well, the best approach is to wrap your preferences class in a helper class and use Dagger to inject it anywhere you need, that way your presenter/model doesn't need to know about the Context.
For example, I have an application module that provides a variaty of singletons, one of them is my Preferences Util class that deals with the Shared preferences.
#Provides
#Singleton
public PreferencesUtil providesPreferences(Application application) {
return new PreferencesUtil(application);
}
Now, anytime I want to use it I just #Inject it:
#Inject
PreferencesUtil prefs;
I think it's worth the learning curve as your MVP project will be more decoupled.
However, if you're willing to forget about the "Presenter doesn't know about Android context" rule you can simply add a getContext method to your view interface and get the Context from the view:
public interface MainActivityMVP {
public interface Model{
}
public interface View{
boolean isPnTokenRegistered();
Context getContext();
}
public interface Presenter{
}
}
And then:
public void sendDataToServer() {
Context context = view.getContext();
}
I've seen some people implement MVP like this, but my personal preference is to use Dagger.
You can also use the application conext, as suggested in the comments.
You are not so far of what you are looking for. You have a model interface, so you have a class that implements this interface, maybe something like this:
MainModel implements MainActivityMVP.Model{
SharedPreferences mPrefs;
MainModel(Context context){
mPrefs =context.getSharedPreferences("preferences",Context.MODE_PRIVATE);
}
}
So, you only need to pass that reference to your presenter, but instead of receive an MainModel class, you can receive an MainActivityMVP.Model instead.
MainActivityMVP.Presenter mainPresenter = new MainPresenter(this, new MainModel(getContext()));
MainPresenter implements MainActivityMVP.Presenter{
MainActivityMVP.View mView;
MainActivityMVP.Model mModel;
MainPresenter(MainActivityMVP.View view, MainActivityMVP.Model model){
mView = view;
mModel = model;
}
}
In this way you don't have any context reference into your presenter, and the reference is into your MainModel and not into your MainActivityMVP.Model.
Add any public method into your presenter/view/model interfaces. You should have something like this:
public interface MainActivityMVP {
public interface Model{
void saveOnSharedPreferences();
}
public interface View{
boolean isPnTokenRegistered();
}
public interface Presenter{
void sendDataToServer();
}
}
I recently started using Dagger 2 to manage the depency injections of my app. In order to make some classes testable, I started creating many classes that I need to inject. I was able to do so, however, the process to get these new classes injected look a little bit complicaded to me. Lets take an example:
I would like to test, for instance, the method onCreateViewHolder() from my RecyclerViewAdapter. To do so, I've created a Factory that returns the ViewHolder based on the given LayoutType:
public class ViewHolderFactor {
public RecyclerView.ViewHolder getViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = this.getLayoutInflater(parent.getContext());
View view;
switch (LayoutType.fromInteger(viewType)) {
case SMALL_VERTICAL:
view = inflater.inflate(R.layout.rsc_util_item_small, parent, false);
return new ViewHolder.ItemViewHolder(view);
case LARGE_VERTICAL:
view = inflater.inflate(R.layout.rsc_util_item_large, parent, false);
return new ViewHolder.ItemViewHolder(view);
}
return null;
}
private LayoutInflater getLayoutInflater(Context context) {
return LayoutInflater.from(context);
}
}
By moving the above code to a separated class, I'm able to perform my unit test using:
#RunWith(JUnit4.class)
public class TestViewHolderFactor extends TestCase {
ViewHolderFactor viewHolderFactor;
#Test
public void testGetViewHolder () {
this.viewHolderFactor = Mockito.mock(ViewHolderFactor.class);
ViewGroup viewGroup = Mockito.mock(ViewGroup.class);
Mockito.when(viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger())).thenCallRealMethod();
Context context = Mockito.mock(Context.class);
Mockito.when(viewGroup.getContext()).thenReturn(context);
LayoutInflater layoutInflater = Mockito.mock(LayoutInflater.class);
Mockito.when(viewHolderFactor.getLayoutInflater(context)).thenReturn(layoutInflater);
Mockito.when(layoutInflater.inflate(R.layout.rsc_util_item_small, viewGroup, false)).thenReturn(Mockito.mock(View.class));
RecyclerView.ViewHolder result = viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger());
assertNotNull(result);
}
}
Problem is: now, to make the app work, I will also have to inject the outter class that holds an instance to the factor (something that I wasn't doing before creating ViewHolderFactor). At end, I will have a Dagger configuration like that:
#Module
public class ModuleBusiness {
#Provides
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}
}
Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter that contains the ViewHolderFactory.
public class
private ViewHolderFactor
#Inject
public CharacterUIService(ViewHolderFactor viewHolderFactor) {
this.mViewHolderFactor = viewHolderFactor;
}
// ...
}
I need a member from ViewHolderFactor in order to inject it.
My worries regards the need to create new global variables and inscrease the number of parameters being passed in the constructor. Is there a better way to inject these subclasses or can I consider it as a good practice in order to allow Unit Tests?
can I consider it as a good practice in order to allow Unit Tests?
First of all—in my personal opinion—I think that you are overdoing the testing. In the test case that you provided you are basically testing the android framework itself. If you want to test the factory, you should test whether a small or large item is returned, rather than the view being not null.
But yes, yours seems like a reasonable approach to allow for unit testing.
Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter
You might want to overthink the role of CharacterUIService. "that creates a new instance" sounds like something that should rather be handled by dagger, since you are already using it.
will also have to inject the outter class that holds an instance to the factory
Why? ViewHolderFactor(sic!) has no dependencies itself. And even if it did, what is stopping you from just creating and injecting it with dagger?
class ViewHolderFactor {
#Inject
ViewHolderFactor() { // allow for constructor injection
}
}
class YourAdapter {
ViewHolderFactor mFactor;
#Inject // allow for constructor injection
YourAdapter (ViewHolderFactor factor) {/**/}
}
// now dagger knows how to create that adapter
And just create let dagger create that adapter?
And generally, if you support constructor injection, then you can remove your #Provides providesSomething() methods. Just remove the whole method. You have an #Inject annotation on the constructor, so make use of that and let dagger write that code for you.
// CharacterUIService can be constructor injected.
#Provides // DAGGER WILL DO THIS FOR YOU
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
// REMOVE ALL OF THIS.
return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}
So while I think you do a good job by making things testable, you should have again a good look on dagger, since you are obviously mixing many things up. Have a good look on constructor injection and make use of it, it saves you a whole lot of work.
I am migrating my apps to MVP. Have taken hints on a static presenter pattern from this konmik
This is my brief MVP strategy. Removed most of the boilerplate and MVP listeners for brevity. This strategy has helped me orientation change proof my background processes. The activity correctly recovers from a normal pause compared to pause which is finishing the activity. Also the Presenter only has application context so it does not hold onto activity context.
I am not a java expert and this is my first foray into MVP and using a static presenter has made me uncomfortable. Am I missing something? My app is working fine and has become much more responsive.
View
public class MainActivity extends Activity{
private static Presenter presenter;
protected void onResume() {
if (presenter == null)
presenter = new Presenter(this.getApplicationContext());
presenter.onSetView(this);
presenter.onResume();
}
protected void onPause() {
presenter.onSetView(null);
if(isFinishing())presenter.onPause();
}
}
Presenter
public class Presenter {
private MainActivity view;
Context context;
public Model model;
public Presenter(Context context) {
this.context = context;
model = new Model(context);
}
public void onSetView(MainActivity view) {
this.view = view;
}
public void onResume(){
model.resume();
}
public void onPause(){
model.pause();
}
}
Model
public class Model {
public Model(Context context){
this.context = context;
}
public void resume(){
//start data acquisition HandlerThreads
}
public void pause(){
//stop HandlerThreads
}
}
I would suggest two things.
Make Model, View, and Presenter into interfaces.
Your MVP-View (an Activity, Fragment, or View) should be so simple it does not need to be tested.
Your MVP-Presenter never directly interacts with the Activity/Fragment/View so it can be tested with JUnit. If you have dependencies on the Android Framework is bad for testing because you need to Mock out Android objects, use emulator, or use a Testing Framework like Roboelectric that can be really slow.
As an example of the interfaces:
interface MVPView {
void setText(String str);
}
interface MVPPresenter {
void onButtonClicked();
void onBind(MVPView view);
void onUnbind();
}
The MVPPresenter class now does not depend on the Android Framework:
class MyPresenter implements MVPPresenter{
MVPView view;
#Override void bind(MVPView view){ this.view = view; }
#Override void unbind() {this.view = null; }
#Override void onButtonClicked(){
view.setText("Button is Clicked!");
}
}
Instead of making the Presenter a static class, I would make it a Retained Fragment. Static objects need to be tracked carefully and removed for GC manually whenever they are not needed (otherwise it's considered a memory leak). By using a retain fragment, it is much easier to control the lifetime of the presenter. When the fragment that owns the retain fragment finishes, the retain fragment is also destroyed and the memory can be GC'd. See here for an example.
Activity, Fragments should have only overidden methods of View interface and other Android Activity, Fragment's methods.
View has methods like navigateToHome, setError, showProgress etc
Presenter interacts with both View and Interactor(has methods like onResume, onItemClicked etc)
Interactor has all the logics and calculations, does time intensive tasks such as db, network etc.
Interactor is android free, can be tested with jUnit.
Activity/fragment implements view, instantiate presenter.
Suggest edits to my understanding. :)
An example is always better than words, right?
https://github.com/antoniolg
You're on the right track, and you are correct to ask about static - whenever you notice that you have written that keyword, it's time to pause and reflect.
The Presenter's life should be tied directly to the Activity's/Fragment's. So if the Activity is cleaned up by GC, so should the presenter. This means that you should not hold a reference to the ApplicationContext in the presenter. It's ok to use the ApplicationContext in the Presenter, but it's important to sever this reference when the Activity is destroyed.
The Presenter should also take the View as a constructor parameter:
public class MainActivity extends Activity implements GameView{
public void onCreate(){
presenter = new GamePresenter(this);
}
}
and the presenter looks like:
public class GamePresenter {
private final GameView view;
public GamePresenter(GameView view){
this.view = view;
}
}
then you can notify the Presenter of the Activity LifeCycle Events like so:
public void onCreate(){
presenter.start();
}
public void onDestroy(){
presenter.stop();
}
or in onResume/onPause - try to keep it symmetrical.
In the end you only have 3 files:
(I'm taking some code from another explanation I gave here but the idea is the same.)
GamePresenter:
public class GamePresenter {
private final GameView view;
public GamePresenter(GameView view){
this.view = view;
NetworkController.addObserver(this);//listen for events coming from the other player for example.
}
public void start(){
applicationContext = GameApplication.getInstance();
}
public void stop(){
applicationContext = null;
}
public void onSwipeRight(){
// blah blah do some logic etc etc
view.moveRight(100);
NetworkController.userMovedRight();
}
public void onNetworkEvent(UserLeftGameEvent event){
// blah blah do some logic etc etc
view.stopGame()
}
}
I'm not sure exactly why you want the ApplicationContext instead of the Activity context, but if there's no special reason for that, then you can alter the void start() method to void start(Context context) and just use the Activity's context instead. To me this would make more sense and also rule out the need to create a singleton in your Application class.
GameView
is an interface
public interface GameView {
void stopGame();
void moveRight(int pixels);
}
GameFragment is a class that extends Fragment and implements GameView AND has a GamePresenter as a member.
public class GameFragment extends Fragment implements GameView {
private GamePresenter presenter;
#Override
public void onCreate(Bundle savedInstanceState){
presenter = new GamePresenter(this);
}
}
The key to this approach is to clearly understand the role of each file.
The Fragment is in control of anything view related (Buttons, TextView etc). It informs the presenter of user interactions.
The Presenter is the engine, it takes the information from the View (in this case it is the Fragment, but notice that this pattern lends itself well to Dependency injection? That's no coincidence. The Presenter doesn't know that the View is a Fragment - it doesn't care) and combines it with the information it is receiving from 'below' (comms, database etc) and then commands the View accordingly.
The View is simply an interface through which the Presenter communicates with the View. Notice that the methods read as commands, not as questions (eg getViewState()) and not to inform (eg onPlayerPositionUpdated()) - commands (eg movePlayerHere(int position)).
I am attempting to implement the MVP pattern in a new Android application. I have two activities, and have defined two View interfaces, one for each of the activities. For example:
public class MyWindow1 implements IView1
public class MyWindow2 implements IView2
And I have a common Presenter class:
public class MainPresenter implements Serializable {
public MainPresenter() {
}
public void setView1(IView1 view1) {
this.view1 = view1;
}
public void setView2(IView2 view2) {
this.view2 = view2;
}
This is all fairly straightforward so far. Now, the activities register with the presenter when they are first created, for example for view1:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_menu);
mainPresenter = new MainPresenter();
mainPresenter.setView1(this);
}
However, I have a problem now - I need to be able to create activity 2 (i.e. view2) and register it with the Presenter. But I cannot do this as the only (it seems?) way to pass objects between activities is the incredibly impractical method of using serialization. Which doesn't work in this case because I have the references back to the views, which would get lost during serialization.
How can I llink a single presenter to two views, ideally without just declaring the presenter as a static global, which I am very close to doing.