I'm making an app which uses RecyclerView to display lists of popular apps or songs (never combined). There are two actions: first updates list just a little bit (adds or removes few items), so using DiffUtil seems a good idea here. But in the second case, all items are changed. Sometimes it's only structural change (when I switch between different apps lists) and sometimes also item types are different (when I switch from apps to songs). I'm new to Android and I'm not sure weather I should use DiffUtil in these cases as well or it only causes unnecessary comparison between lists that are different anyway. And if so, should I use method like notifyDataSetChanged() or just create and set new Adapter with new data?
I've read documentation and some articles on the subject, but I'm still confused, so I'll be very grateful for advice on how to best handle this issue.
Related
I recently came across the concept of using Diffutil and SortedList.Callback<> to change data in instead of notifyDataSetChanged() directly but now I am confused which one to use at which condition.
According to the documentation for sortedList:
A Sorted list implementation that can keep items in order and also notify for changes in the list such that it can be bound to a RecyclerView.Adapter.
It keeps items ordered using the SortedList.Callback.compare(Object, Object) method and uses binary search to retrieve items. If the sorting criteria of your items may change, make sure you call appropriate methods while editing them to avoid data inconsistencies. You can control the order of items and change notifications via the SortedList.Callback parameter.
So, sortedList is a different implementation of List with more functionalities and capabilities. For example:
Keeps last added items. When you update a common List with values that already exist in it tou will end up with duplicate values, while sortedList replaces items that are the same using the Callback's areItemsTheSame.
Updates views smartly. When new items are added, onChange will only be called if one or more elements of the content changed.
Better performance, If you are going to add multiple items to a SortedList, BatchedCallback call converts individual onInserted(index, 1) calls into one onInserted(index, N) if items are added into consecutive indices. This change can help RecyclerView resolve changes much more easily.
On the other hand, according to the documentation for diffUtil:
DiffUtil is a utility class that calculates the difference between two lists and outputs a list of update operations that converts the first list into the second one.
It can be used to calculate updates for a RecyclerView Adapter.
So, diffUtil is a great tool that gives you the ability to compare two lists and find their differences. You can then use these comparison results to do whatever you want, but it doesn't do much more. On the above link you can find more about what's going on under the hood and its performance.
Conclusion:
sortedList is a List with more functionalities, giving you more capabilities than a common List. But, also has different behavior in some cases than a common List.
diffUtil is a great comparison tool for list contents. But, depending on the case it may not provide the best performance. Also, it needs to be used in background thread and the content of the lists it's comparing shouldn't change during the comparison process.
In both, read the documentation before using so as not to have unexpected results.
I want to create 3 level RecyclerView like tree view in kotlin. Is there any tutorial and suggestions please let me know.
I already tried so many times with ExpandableListView and 3 RecyclerView, but didn't find any proper solution.
By a multi-level RecyclerView, do you mean a RecyclerView with paths to different lists that branch depending upon which item has been selected? If that's the case, I would honestly recommend using a single adapter to cycle through multiple lists depending upon user input.
If you have a root list containing two items, each of which opens up its own list with its own unique set of data, you can easily implement code that notifies the adapter of which item in the root list was selected. From there, the adapter can update and switch the view accordingly. This can be applied to series of lists ad nauseam if you so choose, though I can't say I would recommend this kind of method for incredibly complex webs of lists that interact with each other.
Like Ircover said in their comment, I don't think a tree is necessary in this situation either, if only because (1) as stated, it isn't really best practice to do so for the kind of application you're making, and (2) it may unnecessarily over-complicate whatever you're trying to achieve with these branching paths in the first place. If you're willing/able to provide more information about what you're trying to do here, that may help others help you more precisely than I can :)
Full disclosure here: the blog post linked above is not a direct match that will solve your problem - it pertains specifically to displaying different types of data sets (from data classes and what have you,) but employs code that shows how different sets of data can be switched between in a single RecyclerView. Even if it isn't a god-sent solution or is only halfway helpful in solving your problem, I think it can provide some useful information to you.
Maybe a slightly dated question but looking into the same concept and I located this page https://blog.usejournal.com/multi-level-expandable-recycler-view-e75cf1f4ac4b .
They have made a single adapter class to take care of all the navigation and so far seems to be the least complicated example of an expandable RecyclerView, though not in kotlin.
I have been reading a lot about the optimisations in RecyclerView
for quite some time and have learnt a lot of new concepts. One thing that isn't still clear is that can we use both stable ids and DiffUtils together in a RecyclerView. Are there possible benefits/drawbacks of this approach? From what I have read, I think using DiffUtils alone will give all the possible benefits of the reuse of viewHolders and nice animations(correct me if I am wrong). A detailed comparison would be really helpful.
Long story short, DiffUtill fully replaces stableIds approach.
StableIds is a legacy approach from the times when everybody was migrating from ListView to RecyclerView. Along with ItemAnimator it provided an easy approach to get simple predictive animations out of the box. Predictive meaning that RV could deduce itself which items are added/removed or moved when you just called notifyDataSetChanges without bothering about other callbacks.
Once DiffUtil came along there were no need to RV to deduce that, cause DiffUtills tells RV exactly which items are being moved/added/removed.
I use RV in very tough conditions with dozens of item types, and multiple data updates per second, and spend dozen hours debugging RV and animations internals and didn't notice any significant changes in its scrapping/de-scrapping/1-2-3-steps-layouting behaviour when was trying to add stableIds on top of DiffUtil.
Here's a big piece on how animations worked in circa 2015 pre DiffUtill:
https://www.birbit.com/recyclerview-animations-part-1-how-animations-work/
https://www.birbit.com/recyclerview-animations-part-2-behind-the-scenes/
And a little bit more if you're interested in RV internals:
https://android.jlelse.eu/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-404ba3453714
Yes, you can.
If you use androidx.recyclerview.widget.ListAdapter (which you should), you actualy has to provide a DiffUtil. Just look at the constructor. And then nothing is stopping you from providing stable ids.
Actualy, if you don't use stable ids, and submit a new list with event just a minor change, all the items in RecyclerView will "flash". But if you use stable ids, the change will animate nicely.
Also know, that they have no dependency on each other, you'll just probably use the same id (or whatever) field from your items in both getItemId() and DiffUtil.
As per docs DiffUtils:
DiffUtil is a utility class that can calculate the difference between
two lists and output a list of update operations that converts the
first list into the second one.
It can be used to calculate updates for a RecyclerView Adapter. See
ListAdapter and AsyncListDiffer which can compute diffs using DiffUtil
on a background thread.
DiffUtil uses Eugene W. Myers's difference algorithm to calculate the
minimal number of updates to convert one list into another. Myers's
algorithm does not handle items that are moved so DiffUtil runs a
second pass on the result to detect items that were moved.
and as per this Answer StableIds is
Stable IDs allow the ListView to optimize for the case when items remain the same between notifyDataSetChanged calls. The IDs it refers to are the ones returned from getItemId.
Without it, the ListView has to recreate all Views since it can't know if the item IDs are the same between data changes (e.g. if the ID is just the index in the data, it has to recreate everything). With it, it can refrain from recreating Views that kept their item IDs.
Hope you concept will clear now.
In my Android app i'm showing data in a RecyclerView using Room and the Paging library.
My implementation is quite similar to the example in the AsyncPagedListDiffer docs.
The flow is the following:
Data is changed in the database
A corresponding Observer passes the changes to the Adapter:
myLiveData.observe(viewLifecycleOwner, Observer {
myAdapter.submitList(it)
})
The AsyncPagedListDiffer calculates the diff and updates the list accordingly
My problem is the performance hit of step 3.
Even if i insert just one single item to the top of the list, the differ have to check all of the items (which is quite inefficient, especially with larger datasets), whereas a simple notifyItemInserted(0) call would be sufficient.
Is there a way around this behavior?
For example, can i tell the differ that it doesn't have to check all of the items? Or any other solutions to this?
I'd really appreciate any advice.
Today I was thinking about how Android framework works, and I'm not sure about one thing - how developer (me) should program lists to show custom data from few sources. I created few apps, but its important to me to keep my applications clean and fast as possible.
So - I have an app, for example news reader. Im using Fragments, ListFragments, custom layouts for list items and BaseAdapter for showing data in many lists and activities. Nothing new here, and nothing special. At first I download all data from webserver to sqlite in app, so Im sure about it speed and stability. And then what's next?
Should i create List items from Java Lists (List items) and then pass it to baseadapter, or I should only use way SQLite Cursor->list item? What is better to refresh list, add new items and delete them? Remember that i have my ListFragment with baseadapter in separate class.
Im not sure about it, so I clearly dont know how should i refresh and load new items to my list. I just want to keep app clean, without many 3rd party libraries (so i dont care about EndlessList or something like that).
tl;dr
How should I create list for speed purposes, when i use few sources to show data?
I'm not sure whether I understand your full question but about the part which data source to use for an Adapter, here are my thoughts:
I think that depends on the amount of data you are dealing with. If it is sufficiently small you can keep everything in a List in memory, but you also need to be sure that the data won't grow over time.
Using a Cursor as a data source is unfortunately also not a safe option for getting around a possible "Out Of Memory" exception problem. There is a limit per query because of the implementation of the cursor which apparently loads the whole result set into memory.
So if you are dealing with a lot of data or data that potentially grows over time (like messages, user-created items), you need to have an Adapter that internally works with a data window which loads a fixed amount of items into memory at a time. That window always keeps a bunch of items in memory which can be currently viewed and quickly reached with the ListView (by scrolling up and down).
Let's say that window holds for example 200 items.
When the upper / lower bound of that window is reached the Adapter needs to load the next adjacent window. You can observe that kind of behavior in the GMail app ("Loading conversations....").
I would let that data sit in the cursor and not transfer it to a list first, because you can use the cursor like a list.