How to pass context to repository in MVP pattern - android

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();
}
}

Related

Android MVP, same old context problem when getting clipboard data?

So I need to get clipboard data in presenter. This means I need context. Unfortunately I don't have any knowledge of dependency injection if it is the only standard way.
I studied some solutions but they are stated as faulty solutions.
public class MainActivityPresenter implements MainActivityInterfaces.MainToPresenter {
MainActivityInterfaces.PresenterToMain main;
public MainActivityPresenter (MainActivityInterfaces.PresenterToMain main) {
this.main = main;
}
#Override
public void processUrl() {
String url = Utils.getClipboardData(context);
if (url.isEmpty()) {
} else {
}
}
}
And this is the method in Utils class
public static String getClipboardData (Context context) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager cm = (android.text.ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
String result = cm.getText().toString();
return result==null?"":result;
} else {
ClipboardManager cm = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
if (item != null)
return item.getText().toString();
else
return "";
}
}
The right way, The presenter shouldn't know about any specific Android element, ClipboardManager or Context (ie: it should be pure Java). The clipboard logic should be taking place within the View and returning whatever it needs to the Presenter.
The ugly way, if you want to continue with this design, processUrl should take Context, presumably this method is being called from the view? So that is where context can be provided.
#Override
public void processUrl(Context context) {
String url = Utils.getClipboardData(context);
if (url.isEmpty()) {
} else {
}
}
In the view:
presenter.processUrl(getApplicationContext());
Another solution is to ask for context from the presenter to the view:
#Override
public void processUrl() {
final Context context = view.getContext();
String url = Utils.getClipboardData(context);
if (url.isEmpty()) {
} else {
}
}
//In the view
void Context getContext() {
return getApplicationContext();
}
At MVP, when the View propagates an event to the Presenter, it is very common for certain information to be sent to the Presenter. In your case, it would be quite logical to rename your method as follows:
#Override
public void processUrl(String url) {
if (url.isEmpty()) {
} else {
}
}
In this way, the View will be in charge of propagating the information along with the event. And you also keep in the Presenter the ignorance about the peculiarities of the View (Android things), something very important in the MVP pattern.
Good luck!
I know this won't help your question. But aside from MVP, MVVM is much robust and cleaner pattern that doesn't require as much boilerplate code as MVP does, so if you are still in a phase where you are starting a project and deciding or an architecture, I can, as a professional Android developer, only recommend MVVM. MVVM also does the state saves due to LiveData living inside ViewModels so you don't have to painfully add the ability to the presenter via saved instance bundles.
If you need DI in order to inject the context into the presenter (which isn't desirable, context shouldn't get into presenters ideally but sometimes it happens as it is harder to create entities that do the context required job in the presenter), and you are new and unexperienced in DI, then with Java you need to go with Dagger 2, which has a steep learning curve. Switching to Kotlin and then using Koin would be perhaps easier than learning Dagger 2, but if you are into it, try it. In any way it is a required to have DI in a professional projects.
If you don't want to use DI in order to pass the context into the presenter, then pass it into the constructor. I guess the Activity or Fragment is where you initialise your presenter (creating a new instance of it) and where you pass the 'view' concept into the presenter. If you need context in the presenter (again, not the best thing to do but ok, you are a new into patterns I understand), then just pass it into the constructor and set it in the presenter.
Using context in Presenter is not a good idea.
What if I need the Context?
Well, get rid of it. In cases like this, you should ask yourself why you need the context. You may need the context to access shared preferences or resources, for example. But you shouldn’t do that in the presenter: you should access to resources in the view and to preferences in the model. These are just two simple examples, but I can bet that the most of the times it is just a problem of wrong responsibilities.
You can pass url in constructor from View.
public class MainActivityPresenter implements MainActivityInterfaces.MainToPresenter {
MainActivityInterfaces.PresenterToMain main;
private String mUrl;
public MainActivityPresenter (MainActivityInterfaces.PresenterToMain main, String mUrl) {
this.main = main;
this.mUrl = mUrl;
}
#Override
public void processUrl() {
if (mUrl.isEmpty()) {
} else {
}
}
interface SomeView {
void onUrlProcessed();
}
}
In your MainActivity:
class MainActivity {
String url = Utils.getClipboardData(getContext());
}
You can refer other answers here .
Does the presenter having knowledge of the Activity / Context a bad idea in the MVP pattern?
Android MVP: safe use Context in Presenter

Android MVP: which layer should store context variable

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.

Android Dagger 2: Inject versus Provides

I have a question regarding Android Dagger 2 und the usage of #Inject and #Provide annotations. Given are the following two simplified examples:
public class A {
String msg;
public A(String msg){
this.msg = msg;
}
}
public class B {
public A a;
public B(A a){
this.a = a;
}
}
#Module
public class AModule {
#Provides
A providesA(){
return new A("blah");
}
#Provides
B ProvidesB(A a)
{
return new B(a);
}
}
The example is pretty straight forward, I have two methods in my AModule with #Provides annotations. Therefore, Dagger can create an object of B using an instance of A with the string blah.
My second example looks like this:
public class A {
String msg;
public A(String msg){
this.msg = msg;
}
}
public class B {
public A a;
#Inject
public B(A a){
this.a = a;
}
}
#Module
public class AModule {
#Provides
A providesA(){
return new A("blah");
}
}
In this example, Dagger can create an instance of B because an object A can be created using AModule. The instance of B can be created because it's constructor uses the #Inject annotation.
So my question is: What's the difference between those two examples? Both seem to have the same semantics. Does the generated code differ and are there any pitfalls? Or is it just a matter or personal taste or best practices?
They work similarly, and the #Inject style is preferred when you have such an easy choice like in your example. However, this isn't always the case:
If B, which consumes A, is not under your control and not DI-aware, you will have no way to add the #Inject annotation.
If B is an interface, you will need #Provides (or #Binds in newer Dagger versions) to identify which implementation to use.
If you choose not to use your Dagger object graph for every injected parameter, you can call the constructor manually whether it is marked #Inject or not. This might be the case if you want a specific instance or constant as a constructor parameter, but you can't or don't want to set up the binding for the whole object graph.
#Provides allows you to effectively create a factory method, with everything that allows. This is a great way to change which instances are included in your graph, or to effectively add to the class's constructor graph-wide if you can't (or shouldn't) change the class itself.
You can return an existing instance rather than a new one. Note that custom scopes (implemented in Dagger through subcomponents) might be a better fit for common cases, but if you have more complex instance control or use a third-party library that controls instances, you could put that into your factory method.
If you want your binding to return null sometimes that logic can live in a #Provides method. Make sure you annotate the injection sites as #Nullable.
You can choose between implementations with a factory method like a #Provides method, particularly if the choice depends on runtime environment.
You can run post-construction logic, including instance registration or initialization.

What is the best way to inject subclasses using Dagger 2?

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.

Android MVP Strategy

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)).

Categories

Resources