I am working on a social-networking-type app where the user sees a list of "posts" and can interact with them, notably they can "like" them.
The list of posts is in the 10,000s and so we paginate it using Android's paging library (DB + Network), but this is applicable to any situation where there are cached objects that need to be modified.
Loading and paginating the list is working great, very responsive and the built-in animations are nice. The problem is when the user likes a post. In order to like a post, there is a button on the list item. When the user likes (or "unlikes") the post, the button's icon changes and "like count" increments or decrements accordingly and we have to make a call to the appropiate web service. It's all basic social networking stuff and seems pretty simple, but I am struggling with how to update the data correctly and efficiently. So far I've come up with three possibilities but they all suck:
make the request and receive the updated object, then reinsert it into the database. This is easy and ensures that the data is correct but it means that the user has to wait for the request to return before the UI is refreshed, which usually results in the user clicking multiple times on the image (performing multiple requests). So pretty much useless.
modify the UI (the list item view updates it's visual state) but not the backing data, then make the request and insert the result back into the DB. This is a huge PITA as it results in a visual state that is not in sync so if the user scrolls around and comes back before the request completes they will see the original state. I also have to communicate errors back to the view so it can revert its state.
modify the object in memory (change the like count and user-post-like relation), making a copy of the original, reinsert it into the database, make the request, then insert the updated object from the web service or the original object in case of error. This gives instant visual feedback and preserves data integrity but is only mildly less of a PITA.
So for the moment I'm going with 3 because it seems to be the best but I am wondering if there is a better way to handle this? It seems like I'm banging my head on such a basic functionality, surely this problem has been solved a million times before? I am not used to working with caching on CRUD systems (I have implemented basic caching for read-only data) but modifying the cache is turning out to be a bigger obstacle than I expected.
Related
I have a rather peculiar case on my hands, and Im surprised that noone seems to have written about it/something similar for Android (or my Google skills suck)
Situation #1:
User can input text into field1 and field2.
User can also re-arrange items in a list (displayed in a RecyclerView)
Whenever the user does any of the edits, the UI is already showing the updated data (e.g. editing field1 will show the text as the user types it, and the list of items will show them in the new order as the user re-arranges them).
Saving the data right away here will trigger the UI to refresh (to display the same thing) and give the user a bad experience (field1 focus will shift to the first letter, and the app might crash if the user quickly re-arranges list items).
So it makes sense to store the edits and execute them at a later point.
Situation #2:
User can tap plus/minus buttons to increase/decrease a value
User can input text into field3.
As in situation #1 above, editing the field will already have the UI in the updated state. But, in this case - tapping the plus/minus button will also update the data, but the UI will not be updated (unless the data is saved, and the query ran again...).
Problem:
If data is saved immediately as the user performs an edit, besides doing a lot of saves, it makes for a bad user experience as the UI will refresh in some cases whereas its already up to date.
If the edits are tucked away and performed at a later point, the UI wont refresh.
Im using MVVM, so all performed actions are sent to the viewmodel and it decides what to do. I find myself looking for a solution that works differently across different screens of the app, but I know that would just be shooting myself in the foot and then jumping off a bridge. Surely, there must be someone out there that has come across this challenge and had some insights around it?
Ideal solution: One solution that just works for all the different screens. Do you have it? Please let me know.
/ Desperate Android Dev
First of all, let me start by stating that I don't think there is a correct answer here, but you should consider what your own app does and then determine what you need to do.
Let me explain. Consider two application, one that saves TODO items and the other is a banking application.
I will now explain what I think could work for your application, since you have not mentioned explicitly any requirements that contradicts that.
In situations like that, I believe being optimistic is a good idea. Assuming that things will not fail, and when they do, try to back out. What does that mean?
That means, for example, in the scenario you mentioned, user enters something in a field. You should let the UI update automatically (Nothing we do here, that's just Android), when that happens you save those changes either locally, or to a server, doesn't matter.
Of course, you can optimize, instead of saving each letter, throttle the input somehow, but you get the idea.
This can either be a succeed or a failure, because we are optimistic, we let the UI update and the user get the feel that our application is lighting fast. You don't need to reload anything, or refresh anything. The UI should match your Model state now already.
What if the things go south, and your HTTP request or DB update fails for some reason, then you need to take action. But try to keep your reaction appropriate.
You can handle that failure in so many ways, again, depending on how critical what you are doing in your app really is.
You can just show a Toast, or even do nothing if the user action was so trivial.
You can show the user something a bit more concrete if the action is of some significance, maybe a Snakbar with retry and explanation of what happened.
You kill your process and finish all activities -kidding don't ever do that- but showing a very intrusive pop-up and possibly reverting the UI value to the correct one, if what the user was doing is quite critical.
Now this approach doesn't just give the feel that the app is really fast, but it also keeps things simple.
Another advise is don't try to solve problems that don't exist yet, that means don't start implementing background services and job queues for some local persistence jobs that never outlive a view, and could never will.
Instead, use measurements, log those errors and failures with some tool, and use those stats to know what needs to be fixed -if any-
Back to our two applications, this approach might be perfect for a TODO app, however this might not be too good for a banking app.
Assume the user transfers money, we say immediately, ALL GOOD MATE! and then the request fails and your user's landlord kicks him out for never paying rent.
So it all comes to how sensitive the operations your're doing.
Here's something interesting- How do I display information both from my content provider, and some real-time data from the web (which I don't want to save to my content provider?).
1.CursorLoader and CursorAdapter won't do IMO since I don't want to save the information to my content provider.
2.AsyncTask and updating the view in onPostExecute won't work, since right now I am displaying information from my content provider through cursorAdapter etc. and since the screen itself is an AdapterView subclass, when the loading is finished, the view might belong to some other element (recycled)
3.Service won't do for the same reason as #2 (and besides that, in this case, the background thread is coupled with the UI, so that doesn't seem like a natural solution).
**********Optional specific details starting from here if the picture isn't clear******
Say that I have some app which allows users to follow stocks.
I have a content provider, that at the path content://whatever.my.package.name/follows
has some information about which stock the user is following, whether or not it was sent to my server already (so it does have already some 'real time' data displaying through it), the parameters the user is interested in following, etc.
When displaying this information, I want to include some real time information from the web. I already have the necessary method implemented, but I can't think of a natural solution (see above). In particular, the real time data certainly cannot be saved on the same path (/follows) since this isn't a natural part of what I have in mind when I am thinking about the object "follow",but I do want to present the real time information about the stock, and it does relate to the follow presented on the screen (for example, a follow includes a start price, so we want to present the change from that start price to the real time price of the same stock etc).
I'm can't think of a good design I could use, so help will be appreciated :)
If the only thing that stops you from using the content provider is that you don't want to store the informations in it, then don't store it. Remember that a provider is just some abstraction above some data source. Nobody is going to stop you from using a in memory sqlite database for storing the live data.
Then you have two data sources and can build relations on them for displaying purposes like with sqlites attach_database or in code. Of course the live data is gone as soon as the provider is shut down so you must be able to handle that case.
EDIT
Hmm, ok. So touching the provider is a no go. You said the views are adapter views. How about using Volley or something similar to fetch the data in the adapter itself and cache it there. Whenever a view is requested (i.e in 'onBindView' when using RecyclerView) check the adapter cache for the data. If it does not exist or is outdated start fetching the data. When the request returns notify the adapter that the dataset changed. It then would start requesting views again making the next cache probe a hit. If you are fetching the data for each item in the cursor try to pass the index/position of the item to the request so that you can notify the adapter that a specific item has changed.
I'm constructing an API which is going to be used by an Android and an iPhone app. The app gets a list of events which can regularly be updated. There are currently two ideas.
Creating it using pagination so that it first loads the first 10 events to load results on the screen, and when the user scrolls further it should load more events. I then regularly poll the API to see if there are any new events.
First get the paginated list of id's of events (also first 10), after which the apps should get the full event details in separate threads using one call for every event. In that way it can load all events simultaneously which supposedly makes it faster.
I tend to lean more towards the first solution because it's more simple, but somebody else said the second is a way better idea. I have the idea that the separate threads only add complexity to the case and don't increase the speed significantly. I know that the best way to know is to test it, but building both and testing it takes a lot of time. I therefore wonder whether there are any best practices in getting a continuously updated list of events from an API.
So; which of the two do you think would be best and why?
It depends on the amount of data your events contain. If each event description is only a few fields don't bother to load each event in a separate thread, the overhead will kill any possible performance gain - just get all data in the get events request.
If it is a lot of data per event description, you can argue whether you really want to preload all event descriptions before the user selects an event - probably the user will never click on any of the events, then you did load the data for nothing.
That said, it is also not a bad idea to prepare your API to enable both: Get a list of short event descriptions and a call to get event details for a certain event (or a list of event ids), or get a list which contains the full event descriptions.
We have a certain type of app that is connected with server through Socket. Server usualy sends some orders (Adding orders, Updating them, and Deleting). Server sends more the 20 requests per second. And depending on those requests to Android client we have to change ListView Adapter. We are doing that, but nevertheless we have a huge load of CPU.
What do you think can be the root of this problem?
ListView implemented with ViewHolder. We do not know where to measure time and how we can define the bottleneck of this particular app.We were trying to measure parcing of request - it is ok. Also we measure view notification. It seems that it is also not to slow. View then trigered through View.post(). Maybe that MessageQueue to View is too long?
Edit: CPU load is huge even if we are not currently in the ListView (We are not seeing it)
you could refer to this amazing blog post made by Romain Guy (one of the main Android Framework engineers) that shows how to use several optimisation tools. Most of the tools showed are already included in the SDK and measure pretty precisely stuff with times in milliseconds, etc.
Very useful reading for any android developer
edit:
just a wild guess here, considering your description, maybe the CPU load comes from the mobile have to process all the information, even if the information is not relevant to it (you said that depending on the request, you change the adapter) why not make the server check this data and only send to the device what will actually be used?
Well, 20 requests per second is heavy task for mobile device itself (not just ListView).
ListView reloads all contents everytime you add something, so if you adding new items at the bottom of list, then it's better to load new items only when user reaches last item of list.
If you are adding new items at the top of list, then ListView is not really suitable for your purposes. Consider creating your own view instead and providing different implementation for onLayout method.
EDIT
Also, consider using some kind of buffer, which will hold accumulate information and then flush it to UI thread (instead of posting every request to UI thread).
The first time the app is run, I want to have a checkbox list appear where the user selects the list items that they are interested in, and run the program based on that. Every subsequent time the app is run, I want those selected settings to be remembered, or changed with an options menu, in which case the new settings will be remembered. But all I know how to do is make an app go from the beginning every time it is run...
Similarly, my app reads sequentially through all the data in a large, read-only, unchangable database. As of right now, it creates and opens and fetches all the data every time, which takes a few seconds at the start of the program to open up and do anything. Is there a way to get around this, or, is it even a good idea to try to get around this?
To remember the users selection, have a look at SharedPreferences. You can store the selected items there.
To the database: That really depends on your app. If you need all that data at the same time in memory, I guess theres no way around loading it at the start. If you only need parts, it would be a good idea to load a part of the data when required. But without exact knowledge of your app/use case, this is hard to tell.
When you have some sort of "lag" when loading the database: Do you probably load the database in the UI-thread (= without creating a seperate thread for loading)? Thats bad practice since it blocks all UI operations. Have a look at AsyncTasks. Thats a handy class that wraps around a thread and lets you do things in the background without blocking all the UI. After it's done with its task (loading in this case) it provides you a onPostExecute() callback where you can update your UI with the loaded data.