I guess everybody knows the listener interface pattern. I have a class with some favorite items the user can add or remove.
In this class I have:
public static OnFavsChangedListener onFavsChangedListener;
public interface OnFavsChangedListener {
abstract void onFavsChanged();
}
public static void setOnFavsChangedListener(
OnFavsChangedListener listener) {
onFavsChangedListener = listener;
}
public void addToFavorites () {
if (onFavsChangedListener != null) {
onFavsChangedListener.onFavsChanged()
}
}
This works great when I only need to use ONE of those listeners, when I use setOnFavsChangedListener from different Activities, they will overwrite the last one.
My idea was to use an ArrayList of listeners and call them all when my Favs change, but it's hard to not let the ArrayList grow too big (Activities might add a listener on every orientation change / onCreate). I could use a HashMap of IDs and listeners, let every Activity remove it's listener onDestroy, but that seems clumsy.
TL;DR: Whats an elegant way for several Activities to be informed when my Favs change?
This may be overkill for your use case, but for notifying classes about events I use an event bus (specifically https://github.com/greenrobot/EventBus).
This allows you to simply post a message and every class that has registered to receive that type of message gets it. Very simple to use, and pretty small as far as libraries go.
Related
I'm trying to figure out the best way to set up a RealmObject with a RealmResult as one of its fields.
For example, let's say I have two RealmObjects, Goal and Achievement. The Goal object contains fields that define a query of Achievement's the user wants to track (e.g. date range the achievement was created, type of achievement, etc) and has custom methods to extract statistics from those Achievements.
What is the best way for Goal to contain this RealmResult of Achievements? Here are some ways I've thought of doing this:
Have a persisted RealmList field in Goal and update it anytime a field is changed that would change the resulting query. But how would this RealmList get updated if a new Achievement gets added to the realm?
Use #Ignore annotation on a RealmResult<Achievement> field within Goal. Anywhere in Goal where mResult is used, first check if null and requery if needed. This seems like I will be doing a lot of unneccessary querying if I'm using something like a RecyclerView that refetches the object in getItem().
Have a wrapper class that contains a Goal object and the RealmResult<Achievement> as fields. Add a listener to Goal so that anytime a relevant field changes the RealmResult can be requeried.
I'm leaning towards the last one as the cleanest way to keep a valid RealmResult. Am I missing an easier way to accomplish this?
Okay so I'm trying to implement a wrapper class (which I think is similar to the DAO abstraction #EpicPandaForce was mentioning, but I'm not super familiar with that)
public class GoalWrapper {
private RealmResults<Achievements> mResults;
private Goal mGoal;
private Realm mRealm;
public GoalWrapper(Realm realm, Goal goal) {
mRealm = realm;
mGoal = goal;
// TODO: does this need to be removed somewhere? What happens when GoalWrapper gets GC'd?
goal.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel element) {
// rerun the query
findResultForGoal();
}
});
findResultForGoal();
}
/**
* Run a query for the given goal and calculate the result
*/
private void findResultForGoal() {
mResults = mRealm.where(Achievement.class)
.greaterThanOrEqualTo("date", mGoal.getStartDate())
.lessThanOrEqualTo("date", mGoal.getEndDate())
.equalTo("type", mGoal.getAchievementType())
.findAll();
calculateStats();
}
private void calculateStats() {
// Get relevant stats from mResult...
}
}
I haven't tested this code yet but I plan to have a RecyclerView.Adapter with an ArrayList of GoalWrapper objects.
My one concern is that I never remove the listener on mGoal. Do I even need to remove it? What happens in the case that the ArrayList gets GC'ed? I would think that the Goal field and resulting listeners attached to it all get GC'ed as well.
I want to make an app that has a vertical RecyclerView with nested horizontal RecyclerViews. I don't understand how to use properly an MVP pattern in such case. MVP "rule" says that it should be only one View for a screen.
My View interface:
public interface ViewLayer {
void showProductsInCategory(int categoryId, List<ProductModel> productList, PresenterLayer presenter);
void showCategories(List<CategoryModel> categoryItemList, PresenterLayer presenter);
}
Presenter:
public interface PresenterLayer {
void onViewReady();
}
Model:
public interface InteractorLayer {
void getProducts(int categoryId);
void getCategories();
}
Model listener interface:
public interface InteractorListener {
void onProductsLoaded(int id, List<ProductModel> products);
void onCategoriesLoaded(List<CategoryModel> categories);
}
CategoryModel:
public class CategoryModel {
private String categoryName;
private List<ProductModel> productList;
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public List<ProductModel> getProductList() {
return productList;
}
public void setProductList(List<ProductModel> productList) {
this.productList = productList;
}
}
So I have to select each nested RecyclerView by categoryId to add the data to their adapter. Can I create separate Model-View-Presenter interfaces for every horizontal RecyclerView?
UPD:
Step by step
1) MainActivity.onCreate calls presenter.onViewReady()
2) Presenter calls interactorLayer.getCategories()
3) Model calls InteractorListener.onCategoriesLoaded(List<CategoryModel> categories)
4) Presenter calls ViewLayer(MainActivity) showCategories(List<CategoryModel> categoryItemList, PresenterLayer presenter)
5) MainActivity sets that categoryItemList to the outer RecyclerView's adapter. Now each categoryItem has null productList
6) In the method onCategoriesLoaded(...) after ViewLayer.showCategories(...) Presenter calls Model's InteractorLayer.getProducts(i) in the cycle for each Category
7) After any productList loaded Presenter calls ViewLayer's showProductsInCategory(...)
8) MainActivity gets the Adapter of the main RecyclerView, gets a Category item and sets the productList for it.
9) MainActivity calls Adapter's notifyDataSetChanged()
10) The inner RecyclerView sets new productList when onBinding calls
I think its very complicated. What can I do with that?
UPD 03/24/2017
Source code: https://github.com/Lex74/ProductsShop
First, I'd like to state that I don't think of myself as a MVP guru, rather as someone who's striving to understand the pattern,
My favourite MVP reference: The Clean Architecture from Uncle Bob's blog
According to this blog post, there is something called The Dependency Rule:
...source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle...
For example, the Presenter class does not need to know anything about RecyclerView or RecyclerView.Adapter. It needs some interface to pass information to the outer layer.
The methods of the interface depend on the use case: with a List, one would like to be able to
pass a reference to the whole data List (showCategories())
refresh single list items (showProductsInCategory())
So I think the Dependency Rule says, that the ViewLayer interface has to offer methods which satisfy the needs of the [Model layer and the] Presenter layer. As a Presenter, I simply don't care whether the View out there is a ListView or maybe not a View at all but rather some combination of sound and vibration signals.
On the other hand, it seems to be perfectly ok for a View class to know the name (and methods of) its Presenter class, so maybe the PresenterLayer interface is no must-have.
It's entirely up to the View how the data will be offered to the user. A nested View structure still is just a complicated View. So I dont' think one needs to provide nested interfaces.
In some cases with nested Lists, the Presenter might need a method to update an item of the inner List, something like showSingleProductInCategory(ProductModel product, int categoryPosition, int productPosition).
Another interesting question: who keeps (and may modify) the data? In my opinion, the Presenter is responsible for the data, and it should only pass a reference to the data into the View layer or notify it of changes. An Adapter should not have the right to modify the original data list, a Presenter should never have to ask the Adapter "how many items are there?" and I don't really like the idea of two separate data lists. The names of the various notify... methods seem to indicate that I'm on the right track there.
This means Presenter will always hold on to the original data List. If data changes, the Presenter will update its data (may be clear() and "copy the new items", may also be more fine-grained, depending on what ProductLoader is offering) Afterwards, Presenter will notify the Adapter via the ViewLayer interface.
Link to a zip file with the modified Java classes
EDIT
Somehow I doubt that "one View for one screen" will work well for Android. Imagine the typical Master-Detail situation. If the screen is large, you will want to use the space and show both Fragments at once.
So if you have one View (and one Presenter) per Fragment, everything will work for all types of screens. It's up to the Activity to manage the Fragments depending on the screen size.
I've already explained that I like to have the Adapter of some ListView or RecyclerView implement the interface which is required as a callback for the Presenter. (All the Fragment in its role as callback could do would be to pass the information on to the Adapteranyway)
On the other hand, a Fragment may well contain several groups of data. Some of them may be somehow related (like all the songs by one particular artist), others (all those ads...) rather not. The Presenter needs methods to tell the View what to show to the user: one method for the artist, one for the advertisement etc.
So if I had an app with a handful of Fragments, the interface would contain methods like
void showAdvertisement(AdObject ad);
void showArtistInfo(Artist artist);
... and the Presenter would expect some class implementing this specific interface in its Constructor. (Plus the Adapter for the songs), and I'd have the Fragment implement the interface for all the non-collection data.
In a project with several apps, one might consider using generic interfaces
(one for any kind of detail information, one for collections). Then one would have a method showData(T data), and the Presenter in the example above would expect one callback for the advertisement and one for the artist info:
MyPlaylistPresenter (DetailInterface<AdObject> adCallback, DetailInterface<Artist> artistCallback, CollectionInterface<Song> songsCallback){...}
and then in the Fragment one would write:
MyPlaylistPresenter presenter = new MyPlaylistPresenter(this, this, adapter);
A little bit like Lego :), but all in all less interface classes. And methods which do basically the same thing have the same name all over the project, so I think it contributes to maintainability.
Now about your other question:
If your app has a Model on the client side, then I think you're right.
On the other hand, there are projects where the Model is part of the backend. Then the Presenter would be the logical choice.
I am looking for how to share functions and data across multiple activities within a single application. I researched the daylights out of it and find some ideology war between overriding the extend for the application and doing a singleton, neither of which I can find examples sufficient to make me understand. Basically I want to share data and share functions. All activities need the same functions and data so this is not one activity sharing data with another activity. It is all activities needing to have access to the same functions and data.
What I want to know is what is the way to go and how do I do it. I need to see what I need to do in my 34 activities, what the class that is going to be common looks like, and what the Manifest entry needs to be. I also need to be sure the common data area will not be closed by the OS.
This is my first Android - Java program and now find my 15,000 line, 34 activity application needs some structure. I know, should have done things differently but the app works really well with two exceptions. One is that it is structurally a mess. Two is that the fact it is a mess is making it hard to fix one behavior I would like to fix.
This is a GPS based application for racing sailboats. It is timing critical and every activity basically runs a once a second loop inside the location manager onLocationChanged function. That part is fine and I do not want to put the GPS code in one place. The problem is that most activities need to filter the data so a lot of code is copied and pasted to the activities. The filter needs history so it needs to remember a state. There are other functions that are used by several activities so these have been copied as well. Think of a function that averages the last three GPS speed readings. It needs to save some history, do its thing, and give a result. All activities need to do the exact same thing. All this works but the problem is that the averaging starts over every time I switch activities because every activity has its own filter. That gives a glitch in the data that I need to get rid of. I need common place to save the data and hopefully a common place to run the filtering and other functions that are common. If every activity can call the filter function that is using common state data, there will be no glitch across activity changes.
I would appreciate some guidance.
Why you don't just make a Class with only static functions, passing needed Parameters? An example if you want to show an ErrorDialog
public class SharedHelper{
public static Dialog showErrorDialog(Context ctx, String message, String title, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener){
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setMessage(message).setTitle(tilte);
if (okListener != null){
builder.setPositiveButton(R.string.button_positive, okListener);
}
if (cancelListener != null){
builder.setNegativeButton(R.string.button_negative, cancelListener);
}
return builder.show();
}
}
Singletons are (from my point of view) one of the uglyest design pattern and will bite you sooner or later. Putting anything in Application requires you to cast it everytime to the Special Application class you designed. A class with only statics however is very flexible in its usage and doesn't need an instance to work.
For the storage-issue:
lookup "SharedPreferences" & "SQLite" and decide afterwards which storage-type suits your needs more.
For the methods-issue:
This question is a bit more complex and there are different ways to do it. For example you could write a parent-class that implements all your globally needed questions and you let all your activity-classes inherit from it.
public class MyParentActivity extends Activity {
public void myMethod() {
}
}
and:
public class Activity1of34 extends MyParentActivity {
myMethod();
}
I think what this comes down to is not an Android problem but an Object-Oriented Programming problem. If I understand the situation correctly, I'm betting the best solution would be to take your shared filter and create a new Filter class that is instantiated within each Activity (this is likely more manageable than a singleton, but not having seen your use case, it's hard to say for sure). If you need to centrally track the averaging, you can simply create a static variable within the Filter class that maintains the same value during the life of the application. If you really want to maintain that average (even past the application's current lifecycle), you can persist it in a database or other local data options. However, I don't see any reason to put everything in a singleton just to maintain that average. Singletons (and all static data structures) can be potentially troublesome if used incorrectly.
I, for one, do not mind the singleton pattern. Of course as everything else it should not be abused.
This is the construction I use for my shared objects. My app is divided into modules this way but can just as well be used in your case.
public class SharedDataObject {
private Context context;
private static SharedDataObject instance;
public static SharedDataObject getInstance() {
if (instance == null) throw new RuntimeException("Reference to SharedDataObject was null");
return instance;
}
public static SharedDataObject createInstance(Context context) {
if (instance != null) {
return instance;
}
return instance = new SharedDataObject(context.getApplicationContext());
}
// notice the constructor is private
private SharedDataObject(Context context) {
this.context = context;
}
...
public void myMethod() {
// do stuff
}
}
Notice that it uses the application context, that means among other things, means that the context owned by SharedDataObject cannot be used for GUI operations. But, the context will live for the entire lifetime of the application, which is nice.
Furthermore I hate having to pass a context everytime I wish to call methods on my SharedDataObject, thus I have a splashscreen calling SharedDataObject.createInstance() on all my modules.
Once an instance is create, I can call:
SharedDataObject.getInstance().myMethod();
Anywhere in my code, regardless of a context being present or not (from the place calling this code that is).
I have more than 50+ activities in a single project so each and every time I need to write code to move a single activity from one to other.
What is in my mind, may I make a single function which can move more than one activity from a single static method of a class?
Like given in code below.
public static void moveActivity(Parameters)
{
//Code to move activity
}
Or may I follow any other idea for the same.
Any help would be really appreciated.
Thanks in advance.
What is in my mind, may I make a single function which can move more than one activity from a single static method of a class?
I believe not because startActivity needs a Context which can't be used in a static context, I believe.
Or may I follow any other idea for the same.
This depends on what you are doing but maybe ViewPager and/or Fragments could make your life easier. If you have that many Activities then most likely you won't want to try and manage them all from one function anyway because you have to consider extras, flags, etc... You will have two lines to start an Activity (sometimes more) just write the code. What you are trying to do will most likely result in more headaches, IMHO.
If there is a lot of the same data that you are passing around and that is your concern then you can use Bundles to pass them all around.
Try this:
public class Utils {
public static void launchActivity(
Class<? extends Activity> nextActivityClass,
Activity currentActivity, Map<String, Integer> extrasMap) {
Intent launchIntent = new Intent(currentActivity, nextActivityClass);
if (extrasMap != null && extrasMap.size() > 0) {
Set<String> keys = extrasMap.keySet();
for (String key : keys) {
launchIntent.putExtra(key, extrasMap.get(key));
}
}
launchIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
currentActivity.startActivity(launchIntent);
}
}
To use it:
From your activity class, make this call:
Utils.launchActivity(ActivityB.class, this, extrasMap);
im am having difficulties to update a ListActivity when the underlying data changes.
I am using a custom (list) adapter (CustomListAdapter) derived vom BaseAdapter to fill a ListActivity with custom list elements (CustomListElement).
The elements in question can have their underlying data changed over time, either by user interaction or by changes in an underlying database. In order to announce said changes, the CustomListElement and CustomListAdapter objects can register DataSetObserver objects.
This is in essence done like this (unfortunately posting the entire code would be overkill):
public class CustomListElement extends DataSetObservable {
protected Object value;
public void setValue(Object newValue) {
this.value = newValue;
notifyChanged();
}
}
Hence a CustomListElement provides registerDataSetObserver by inheritance from DataSetObservable and announces changes by means of it's notifyChanged() method.
And for the CustomListAdapter:
public class CustomListAdaper extends BaseAdapter {
protected List<CustomListElement> elementList;
#Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
for (CustomListElement element : elementList)
element.registerDataSetObserver(observer);
}
}
I.e. the observers are "handed through".
Now, when invoking
setListAdapter(new CustomListAdapter(customElementList));
within a ListActivity this should register an android.widget.AbsListView.AdapterDataSetObserver within the setAdapter method of ListView (invoked from setListAdapter of ListActivity).
Upon notifying the registered DataSetObserver objects of any change the onChanged method of the AdapterDataSetObserver and therefor requestLayout of the ListView should be invoked. This should (to my understanding) refresh the ListView.
However, the ListView is not updated with the new data.
I realize it has been pointed out that notifyDataSetChanged and (maybe) notifyChanged should be run within a runOnUiThread environment, however that does not seem to fix the issue.
I also realize that similar questions came up, but not with this specific set of android classes, and with unsatisfying answers.
Am i missing anything? Any insight into why this breaks and how to fix it is greatly appreciated.
The registerDataSetObserver() part of the Adapter interface is for any external objects who might be interested to know when the data set changes. A ListView shouldn't really be interested in these methods... if it's BaseAdapter content changes, you call BaseAdapter.notifyDataSetChanged() which will tell the ListView to update itself.
In other words you only need to make the following tiny change:
public void setValue(Object newValue) {
this.value = newValue;
notifyDataSetChanged();
}
Actually, since you're changing the state of an existing item (rather than adding new ones etc) then notifyDataSetInvalidated() would be a better choice.
And of course you don't need any of that DataSetObserver stuff unless you actually do have other objects elsewhere that need to know about this data.
The issue is resolved. The problem was in fact at a different point (an intermediate class that was not mentioned here didn't react appropriately to changes). The initial code works beautifully.
Thanks alot for the effort,
adapter.notifyDataSetChanged();