Android MVP WeakRefrence - android

In mvp we save that reference of an activity in a weak reference.
WeakReference<Activity> view = new WeakReference<Activity>(activity);
If we lose the reference. can we get it back?

If you lose the reference to your Activity it means the activity was garbage collected and it doesn't exist anymore. You can't get back what doesn't exist.
Ex. If this happens because of configuration change it means a new activity was created.
You need a way to attach the newly created view to the same presenter.
If you are looking for libraries to help you, take a look at mosby and nucleus.

I don't think you should be saving a reference to an Activity in MVP at all - doesn't matter if it's hard or weak!
I'm assuming you're storing this reference in the Presenter. To really decouple the layers you should create an interface that describes your View (Activity) and use it instead of the activity.
So you'd do:
public interface LoginView {
displayUsernameError(String error);
displayPasswordError(String error);
openMainScreen();
}
Your Activity should implement the interface from above.
public class LoginActivity implements LoginView {
...
}
In your presenter you'd have:
class LoginPresenter {
private LoginView mView;
public LoginPresenter(LoginView view) {
mView = view;
}
public onLoginButtonClicked(String username, char[] password) {
...
mView.openMainScreen();
}
}
Immediate benefits of doing this:
The different layers are really decoupled. You can change your Activity (say you decide to use Fragments instead) without touching your Presenter.
Your presenter is fully testable using JUnit only! No need to use anything fancy to verify your interactions are correct, just plain Mockito to mock the LoginView.
One other point to note - are you sure you want your Presenter to outlive your View? There are some situations when that can't be avoided, but in most cases they have the same life span - when the View is destroyed the Presenter should be as well.

How did you set the reference in the first place?
You should be setting it with a setter method in Activity's onCreate.
This "setter" method is often called "attach" or "bind".
fun attach(view: View) {
this.view = view
}
So when new Activity is created due to configuration change, it will set itself to the presenter again. Note that you might be dealing with a new instance of the presenter too. However, based on your question I believe you want to attach the newly created activity to the same instance of presenter. If you got your presenter scoped right, this will work for both cases :)

Related

MVVM - How to cleanly incorporate AlertDialog?

I have a FragmentOne which contains the MVVM pattern. The Fragment has it's own ViewModel, FragmentOneViewModel. I am unsure on how to cleanly incorporate an AlertDialog in my case.
In the Fragment it contains a RecyclerViewAdapter, with each row that contains a Button to create an AlertDialog. Do I need a ViewModel for the AlertDialog?
Or can I use an interface callback and implement it on my Fragment, which will be called when the user clicks on buttons in the AlertDialog. And then use that input to call the Fragment's ViewModel?:
public class ActivateUserDialog extends AlertDialog {
//Should I use callback methods?
private Context mContext;
public interface DialogOnClickListener{
void onCancelBtnClick();
void onConfirmBtnClick(String amount);
}
public ActivateUserDialog(#NonNull Context context) {
super(context);
this.mContext = context;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_activate);
ButterKnife.bind(this);
}
#OnClick(R.id.btn_cancel)
public void onBtnDismiss() {
this.dismiss();
}
#OnClick(R.id.btn_confirm)
public void onBtnConfirm() {
//Call the fragment
MainActivity activity = (MainActivity) mContext;
MembersFragment fragment = (MembersFragment) .... get the fragment;
fragment.onConfirmBtnClick(amount);
}
}
And then calling the viewmodel from the callback method:
//Fragment class:
public class MembersFragment extends Fragment implements ActivateUserDialog.DialogOnClickListener
#Override
public void onConfirmBtnClick(String amount){
mViewModel.activateUser(amount);
}
Is this a clean way to do it?
I don't think there's a single answer to it and would say it's opinionated. Both would be valid in my opinion.
You also need to consider that architecture components don't work "out of the box" with AlertDialogs, so even if you want a VM for it you will have to retrieve it through the fragment/activity (basically a ViewModelProvider) or use some other solution like DI.
You would also have to manage the lifecycle of the AlertDialog to gain some of the benefits of the ViewModel.
Here are possible solutions:
Consider the AlertDialog as part of the View of FragmentOne and communicate with it via callbacks. In the implementation communicate with FragmentOneViewModel.
Use a DialogFragment with a ViewModel instead. Since DialogFragment is basically just a Fragment you get all the benefits of the architecture components ViewModel. You can either share FragmentOneViewModel between the two or create a new ViewModel dedicated to the dialog. Whether to share or create a new one is up to your needs but I would consider the complexity of the Dialog and whether it should be stand-alone. If you decide to share the ViewModel, be careful which ViewModelProvider you use to make sure they are actually shared and not a different instance.
As for communicating back from the AlertDialog, I would suggest using an interface with callbacks instead of passing Context and casting directly.
You can accept an implementation in the constructor.
You want to use a dialog box on some click in recycler-view adapter which is based on MVVM pattern and want some callback on adapter after dialog box action right?
Yes this is what I am doing in my code above, but I don't know if
this is the best practice way to do it with Dialog – DIRTY DAVE
As I think its a good way easy to understand and maintainable. However, dialog box is part of the UI and so interaction with it. So it should be in the Fragment. The fragment then interacting with the view-model is a reliable way to do things as that's the main home for your logic.
As the MVVM design pattern is similar to the well known MVC pattern in that the M (Model) and V (View) are relatively the same. The only difference resides between the C (Controller) and the VM (View Model).
Model
Represents the Data + State + Business logic. It is not tied to the view nor to the controller, which makes it reusable in many contexts.
View
Binds to observable variables and actions exposed by the View Model. It is possible for multiple views to bind to a single View Model.
View Model
Responsible for wrapping the model and preparing observable data needed by the view. It also provides hooks for the view to pass events to the model. An important thing to keep in mind is that the View Model is not tied to the view.
Source

Android ViewModel for a custom view

I would like to refactor my Custom View to use android architecture components. However, I see that
ViewModelProviders.of(...)
takes only Activity or fragment. Any idea how to make it work? Should I use fragment instead of Custom View?
It is possible to get obtain a ViewModel instance in View, although it's not recommended. As per this post:
While it is easy enough to obtain the ViewModels inside an Activity or a Fragment, it is not straightforward to obtain this instance inside a View. The main reason behind this is because Views are supposed to be independent of all processing and even if all your logic is inside a ViewModel, the fact that you are accessing that ViewModel inside the View makes it reliant on something that it shouldn't. The recommended way of controlling a View is to pass parameters to it based on the state of the ViewModel from the Fragment or the Activity.
The point is to try to get an Activity from the context:
override val activity: FragmentActivity by lazy {
try {
context as FragmentActivity
} catch (exception: ClassCastException) {
throw ClassCastException("Please ensure that the provided Context is a valid FragmentActivity")
}
}
override var viewModel = ViewModelProvider(activity).get(SharedViewModel::class.java)
As mentioned thought, I'd try to avoid this approach if possible.

How to connect View and ViewModel with respect to screen rotation?

One promise of the ViewModel is, that it survives cases like rotation of the screen. I still try to figure out how to organise this in practice.
On certain events of the model the View should update. There are two major options:
The ViewModel updates the View.
The View observes the ViewModel and updates itself.
In the first case the ViewModel needs a link to the View. I could inject the View into the ViewModel, yet my feeling is it would be better to inject the VieModel into the View.
What is the better style to join them?
Then after rotation the onCreate() method is called again triggering initialisations of the ViewModel a second time. I need to check for this else I am in danger to register listeners to the actual model twice and thrice and similar issues. I may even need to clean up relations to the old view first.
This checking feels kind of unclean. I would expect a dedicated API for this in the ViewModel, if this would be a standard practice. Without I have the feeling to be on the wrong track.
What are good patterns to deal with this in a clean standard way?
Soo.. You don't really have to connect the ViewModel and the Activity/Fragment "with respect to screen rotation", you get that for free - that's one of the perks.
The official documentation is really good.
You connect a ViewModel to your view in onCreate() by something like
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
updateUI()
});
}
}
And while it is true as you say that orientation change will trigger onCreate() again, it's not true that this will create a new ViewModel. The MyViewModel is only created the first time around in onCreate. Re-created activities receive the same MyViewModel instance created by the first activity. This is even true for different fragments/activities referencing the same ViewModel.
You should never ever inject a view into the ViewModel. It's the equivalent of drowning puppies. If you need a context in the ViewModel, extend AndroidViewModel instead (and pass it the Application).
What you do is that you create a ViewModel that holds all state. And handles fetching data from network or disk or what not. All that is not UI related goes in to the ViewModel (as a rule of thumb). All view updating stuff goes into the activity/fragment.
A ViewModel for the example above might look like
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
This often means that click events should probably be passed down into the ViewModel so that it can massage the data. And the view will just react to the updated (massaged) data.

Context in MVVM in android

I have started working on MVVM architecture for android application.I have a doubt that is it right to pass the context to view model ? If not then how can my view model can access the context if needed.
I am doing the following things:
Feed data using some EditText.
Send this data to View model.
View model send this data to repository
Repository storing this data to shared preferences of the device.
As shared preferences required context to instantiate the object.
I am new to this architecture any guidance would be helpful for me, thanks in advance.
I think the use of ApplicationContext is ok, You can extend your ViewModel from AndroidViewModel and whenever you need a reference to the context use getApplication() methods.
Even better, if your using dagger, you don't need this at all, you just inject your ApplicationContext where ever you need it. It can be in your view model or a utility class that handles shared preference, etc.
UPDATE 2019
I don't want to mislead anyone with my old post, so figured I better update it.
With the latest release of the architectural components and JetPack, MVVM is now a real competitor for actual proper structure of breaking apart the code base in a very clean way.
The View is the Activity or Fragment along with the xml that is inflated basically (that's a bit of a weird MVVM, I know, but let's roll with it).
The ViewModel is the actual ViewModel classes offered now with lifecycle scopes and observers. Due to these new features, and life cycle management tools, the ViewModel is very nice at maintaining the data for the view while accessing the Repository Layer of Room DB or Retro API for fetching appropriate LiveData, Observable Data or just standard Models.
I'll leave this here as I feel it was very relevant during the earlier pre Architecture Implementation Days, but if you are not using the Architecture Components and JetPack, Boy are you missing out lol. Code is getting so much cleaner and smaller. :)
OLD REPLY
This is a long debated discussion among the Android community. When we refer to MVC it is obvious that Models (aka data holding objects) are the M, Controllers = C for the Activity classes (in iOS they actually call them UIViewControllers) and the V for the View in Android is the XML file itself. Now some would argue the architectural breakdown of what represents what. However, let's move past this and discuss MVVM.
MVVM has been around for many years now. There have been a few attempts in the past to bring it to Android through 3rd party binding tools, but it ends up more hacky then it is worth.
Recently with the release of native Data Binding Android is finally capable of doing a somewhat clean implementation of MVVM. So there are a couple options here. You can do
M = Models (data holding objects)
V = Views (the XML file itself representing the UI)
VM = This is where debates occur.
Some say in order to be a "true" ViewModel one must have true separation from the Presentation layer and the Activity class itself has lifecycle callbacks thus making it unworthy of being known as the ViewModel.
Others would point out that in order to handle most of the actions triggered from the ViewModel one must have an awareness of onActivityResult, onNewIntent, onBroadcastReceived, onPause or other life cycle handlings to appropriately manage the user experience. So On my team we consider the Activity as the ViewModel. Otherwise you are passing the activity down to a viewmodel and tightly coupling the two anyways, making a giant hideous maintenance nightmare of code.
So you if you want my opinion, stick to treating the Activity as your ViewModel. Get your data to the ViewModel just like any other binding technology like INotifyPropertyChanged in WPF. You do it through your Binding.
I do two things to make this happen. One I have an Activity variable in the XML layout to inject in the onCreate for the binding setup which gives the XML direct binding rights to any observable properties in the viewModel aka the activity. Then I inject whatever variables I need to make use of as well for example you may have a WeatherModel that populates a forecast that lives within the Activity that you would also set in the onCreate to allow the XML access to both it's ViewModel (aka activity) and it's viewModel's Objects.
The other alternative is to make a ViewModel object and inject it in the XML in the onCreate of your Activity and then continue to call back and forth from the activity to the viewmodel for handling actions and lifecycles and feel free to manage that nightmare haha, I did an entire project this way and by the end of it, I redid it all to avoid all the duplication of coding efforts and hideous back and forths.
Goodluck and I hope that helps.
A very good question, and it is not as simple as it seems!
You can see an example from Google team here.
They solved the problem with the help of the factory.
There it is pass (of course) the Application context (not Activity context !).
A small problem - and so much boilerplate code!
My decision:
public class MainApplication extends Application {
public void onCreate() {
AppSharedPref sharedPref = AppSharedPref.getInstance(PreferenceManager.getDefaultSharedPreferences(this));
AppRepository.getInstance(sharedPref);
Repository is singltone (a lot of code is skipped for brevity):
public class AppRepository implements AppDataSource {
public static AppRepository getInstance(#NonNull AppSharedPref sharedPref) {
if (INSTANCE == null) {
INSTANCE = new AppRepository(sharedPref);
}
return INSTANCE;
}
In ViewModel call:
public class MyViewModel extends AndroidViewModel {
// constructor
public MyViewModel(#NonNull Application application) {
repository = AppRepository.getInstance(.....);
}
Look into Dagger 2!
That's true , you definitively shouldn't pass Activity to your xml or ViewModel. It will make your ViewModel no better than these 2000-line activities that we are trying to move away from with this architecture.
The solution we have in our MVVM project is to inject SharedPreferences with Dagger. You can of corse use ApplicationContext as well but do you need multiple instances of SharedPreferences in the project??
We a have a utility class for SharedPreferences and it is nice to keep it a singleton and inject wherever you need it.
I use MVVM in my application. I always try not to use Context inside my View Model. I also encountered the problem with SharedPreferences requiring a context to access the preference files. One of my solutions without using Dagger is to create a Preference utility class that will have reference to the application context. You initialize this utility class in you Application class. You get reference to the shared preference through a public static method provided by the utility class. You can directly call the utility class from your repository class. I prefer to contain all logic related to data storage in the repository class that's why i call the sharedpreference utility class in my repository class.
PreferenceManager.java
public class PreferenceManager {
private static SharedPreferences mSharedpreferences;
private PreferenceManager() {}
public static void initialize(Context context) {
mSharedpreferences= context.getSharedPreferences(context.getPackageName(),
Context.MODE_PRIVATE);
}
public static SharedPreferences getSharedPreferences() {
return mSharedpreferences;
}
}
App.java
public class App extends Application {
#Override
public void onCreate() {
PreferenceManager.initialize(this);
}
}
Repository.java
public class Repository {
public void someMethod() {
PreferenceManager.getSharedPreferences.edit.putBoolean("sample", true).apply();
}
You should use AndroidViewModel() in the ViewModel, pass application:Applicatoin, and use getApplication() to get the context.
ViewModel Example:
class MainTrendsViewModel (application: Application) : AndroidViewModel(application) {
// ViewModel must extend AndroidViewModel(), and pass application to it.
// Code here and
// use getApplication() to get context
}
Other anwer that work for me that you could reference: reference answer that works for me
You should use AndroidViewModel class or keep reference to Application context in yours ViewModel implementation in this case.
ViewModel class was designed to keep data persistent between different instances of Activity through its lifecircle and storing reference to one of Activity instance context really doesn't make sense.

Using static instead of parcelable/serialize -- is this bad?

I need to keep a reference to an object between different activities. The two said mentions seem to create new objects from activity to activity. This is a problem because changes to the object in the "child" activities don't persist once the parent activity gets focus. My solution to this was just to create a static getter in the parent-most activity which the child activities call and work with. Is this the incorrect way to go about this?
If you want to share a single instance of an object between activities, you can create a singleton class. The only thing wrong with using your parent-most activity class to implement the singleton might be that it might violate the single responsibility principle.
You can make your object persistent throughout the whole application lifecycle by making it a field in your Application-derived class.
public class MyAppication extends Application {
private Object mMyData;
public setData(Object data) {
mMyData = data;
}
public Object getData() {
return mMyData;
}
}
Then ((MyApplication)getAppllication()).setData or getData()
This way you can exchange data within the application because MyApplication will always exist.
You'll also have to add MyApplcation to manifest
You should create a Singleton, this has a single instance whenever you talk to it. (Just like your describing).
Here's one I made earlier : https://stackoverflow.com/a/6539080/413127

Categories

Resources