I have an Activity with two Fragments in it.
The activity (MainActivity) retrieves data from an open weather api. I have implemented MVP for this in which:
Model contains all the response objects from the API
View is the Activity
Presenter contains MainPresenter, MainPresenterImpl, MainView, GetDataInteractor and GetDataInteractorImpl.
So, the activity gets the data from the web service. Both fragments will display data from the data retrieved in the activity.
What is the best practice using MVP in this situation? I know how to pass data between fragments <-> activity via interface/callbacks, my question is does this behaviour change when implementing MVP?
The activity/fragments should be considered as just the view in the MVP model. This means that they should just show data and receive user interactions.
It is ok to communicate activity and fragments via interface/callbacks.
But, it is not an activity/fragment responsibility to call the API services.
The presenter should be responsible to call the api services.
So, the presenter should expose a method like loadXXX, internally it would make the call to the service. When the response is received, the presenter should call view.showXXX with the results of the service. The activity/fragment should call this loadXXX method and implement the showXXX.
Usually, the presenter is created or injected into the activity/fragment.
The activity/fragment has to implement an interface exposed by the presenter, and the presenter holds a weak reference of this interface, so that it can callback.
When the user interacts with the screen, for example, an onClick on a button, the activity/fragment calls the corresponding method in the presenter, e.g. presenter.loadUserDetails() the presenter tells the view to show as loading, e.g. view.showAsLoading() because it has to do its stuff: maybe validating something or loading data from an api service and finally callback with the results to the view, e.g. view.showUserDetails(userDetails).
To summarize, an example, in code of the various parts of MVP:
Activity/Fragment represents just the View of MVP:
public class MyActivity extends AppCompatActivity implements MyPresenter.View {
private MyPresenter mPresenter;
public onCreate() {
...
mPresenter = new MyPresenter(this); // Or inject it and then set the view.
}
public void onClick(View v) {
mPresenter.loadXXX(param1, param2);
}
// MyPresenter.View methods
public void showAsLoading() {
...
}
public void showUserDetails(UserDetails userDetails) {
...
}
}
Model:
public class UserDetails {
...
}
Presenter:
public class MyPresenter {
private WeakReference<MyPresenter.View> mWeakView;
public MyPresenter(MyPresenter.View view) {
mWeakView = new WeakReference(view);
}
public void loadXXX(String param1, String param2) {
MyPresenter.View view = mWeakView.get();
if (view != null) {
view.showAsLoading();
// Do stuff, e.g. make the Api call and finally call view.showUserDetails(userDetails);
}
}
interface View {
void showAsLoading();
void showUserDetails(UserDetails userDetails);
}
}
Related
I'm using Retrofit with RxJava2 to obtain some data from a Rest API. I want to use a SwipeRefreshLayout to update the view and I'm using a ViewModel to handle the API call, so I want to implement a method in there to refresh the data programmatically.
I want to obtain something like this https://stackoverflow.com/a/34276564/6787552 but instead of having a periodic trigger, I want to do that programmatically when the user pull to refresh.
That's the ViewModel:
public class DashboardViewModel extends ViewModel {
public final Single<Dashboard> dashboard;
public DashboardViewModel() {
dashboard = Api.getDashboard();
refresh();
}
public void refresh() {
// Refresh data
}
}
And in the DashboardFragment:
#Override
public View onCreateView(...) {
...
viewModel.dashboard
.observeOn(AndroidSchedulers.mainThread())
.subscribe(dashboard -> {
binding.setDashboard(dashboard);
binding.swipeRefreshLayout.setRefreshing(false);
});
binding.swipeRefreshLayout.setOnRefreshListener(() -> viewModel.refresh());
...
}
Thank you in advance!
EDIT:
That's what I ended up doing:
public class DashboardViewModel extends ViewModel {
private final BehaviorSubject<Dashboard> dashboard;
public DashboardViewModel() {
dashboard = BehaviorSubject.createDefault(Api.getDashboard());
}
public void refresh() {
// I use a Object because null values are not supported
dashboard.onNext(Api.getDashboard());
}
public Observable<Dashboard> getDashboard(){
return dashboard;
}
}
And then in the DashboardFragment just subscribe to viewModel.getDashbaord()
I'm not 100% sure that I understood what you want to do but if I got the question right, you can do something like this:
put a subject inside the model (probably a BehaviorSubject?)
expose it as an observable to the
view and subscribe to it (instead of subscribing to the single)
in the model, when you
receive a new call to refresh() from the ui, do something like
subject.onNext(Api.getDashboard())
in this way, each call to refresh will cause the emission of a new dashboard, and that will be properly bound by the subscription in the view.
Previously, I was using activities in my project and was sending data using Intent from one activity to another which works perfectly fine.
Now requirement changes, and I have to show all things on Dialogs, instead of activities, so there will separate 3-4 dialog class and single activity.
Now I want the same flow on dialog also, but there is a problem to pass data temporarily exactly how intent works!
I tried with Singleton, but the problem is it remains data until the whole lifecycle, but I don't want that.
I can't use the interface also because there are lots of things to pass.
Also, I can't use bundle fundle n all those, because this all depends on runtime, I meant it depends upon if user fill input
Question: How can I pass data from one class to another class or activity? and it should not save value for the whole lifecycle.
statically sending data is an option but its not good way, because memory to static variables is assigned at Application level and can be cleared when memory needed. The best way is to use
Object Oriented approach
For example if you have a class, You can send data in class constructor, or can send it through function call
class class1
{
public class1(Object data) { // constructor
// you can use this data
}
//// Or through function call
public void func(Object data) { // this method can be called by other classes which has its object
// you can use this data
}
}
Now lets assume you have another class
class class2
{
class1 obj = new class1(your_data_object); // if you want to send through constructor
void someMethod() {
obj.func(your_data_object); // send data whatever you want to send
}
}
Obviously your case will not be as simple as my example, but to handle complex cases you can implement interfaces.
Interface Example
define an interface
interface myListener {
public void listen(Object data);
}
now lets say you want to call class2 method from class1. then class2 must implement this interface.
public class class2 implements myListener {
#override
public void listen(Object data)
{
/// you got data here, do whatever you want to do that with that data.
}
}
Now in class1 if you have interface object you can call class2 method
interfaceRef.listen(your_data);
Try with EventBus or BroadCastReceivers to pass data accordingly in local variables.
EventBus is a publish/subscribe event bus for Android and Java. EventBus... simplifies the communication between components. decouples event senders and receivers. performs well with Activities, Fragments, and background threads.
http://greenrobot.org/eventbus
First Register to EventBus in your Activity
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
Now pass the data from anywhere ,whether it is activity/fragment/background service etc etc etc like :
EventBus.getDefault().postSticky(new MessageEvent("your data here");
Now in your activity receive this message like :
#Subscribe(sticky = true,threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Log.e("TAG","Event Received");
Log.e("TAG",event.getData);
}
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 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.