Open In App

DiffUtil in RecyclerView in Android

Improve
Improve
Like Article
Like
Save
Share
Report

Have you ever created a List in Android? What did you use to make it? ListView or RecyclerView are two types of views. If you are an Android Developer it’s sure you’ve used RecyclerView at some point. In this article, we’ll go through how to update the RecyclerView with DiffUtils. 

What exactly is RecyclerView? 

RecyclerView is a more adaptable and efficient version of ListView. It is a container for displaying a larger data set of views that can be recycled and scrolled very quickly. 

Before we go into Diff Util, let’s have a look at the RecyclerView implementation.

RecyclerView Implementation in Brief

Let’s make an Activity MainActivity and include the following code in the activity main.xml file:

XML




<androidx.constraintlayout.widget.ConstraintLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".GeeksforGeeksActivity">
 
  <androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/gfgRecyclerView"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>
 
</androidx.constraintlayout.widget.ConstraintLayout>


Let’s make a data model class and a Data Source now,

Kotlin




data class GeeksCourses(val courseNumber: Int, val courseRating: Int, val courseName: String)


and the data source appears to be,

Kotlin




object geeksforGeeks {
    val courseList: List<Course>
        get() {
            val course = ArrayList<Rating>()
            course.add(Rating(1, 10, "GeeksforGeeks"))
            course.add(Rating(2, 12, "Android Dev"))
            course.add(Rating(3, 5, "DSA"))
            return course
        }
}


Let’s make an adaptor now to set the list in RecyclerView.

Kotlin




class CourseAdapter : RecyclerView.Adapter<CourseAdapter.ViewHolder>() {
    private val courses = ArrayList<Course>()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val view = inflater.inflate(R.layout.course_list, parent, false)
        return ViewHolder(view)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val Course = courses[position]
        holder.name_text.text = Course.name
    }
    fun setData(courses: List<Course>) {
       courses.clear()
       courses.addAll(courses)
    }
    override fun getItemCount(): Int {
        return courses.size
    }
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val name_text: TextView = itemView.findViewById(R.id.name_text)
    }
}


What if we need to update the list with fresh information?

We’ll refer to it as,

Kotlin




adapter.setData(/** any new data on courses**/)


and make a call from MainActivity,

Kotlin




adapter.notifyDataSetChanged()


GeekTip #1: The recyclerview will be updated with the new set of data as a result of this.

But, since notifyDataSetChanged was performing the work for you, why did you require DiffUtils? Let’s talk about it.

  • There is no way for the RecyclerView to know what the real changes are if notifyDataSetChanged() is used. As a result, all visible views are rebuilt. This is an extremely costly surgery.
  • During this procedure, a new instance of the adapter is generated. As a result, the process is highly time-consuming.

To address this, Android introduced DiffUtils as part of its support library.

It’s a utility class that helps us to perform complex tasks easily, Eugene Myers’ algorithm is the foundation of DiffUtils.

DiffUtils is used to track changes made to the RecyclerView Adapter. 

DiffUtil notifies the RecyclerView of any changes to the data set using the following methods:

  1. notifyItemMoved
  2. notifyItemRangeChanged
  3. notifyItemRangeInserted
  4. notifyItemRangeRemoved

These techniques are significantly more efficient than notifyDataSetChanged(). However, in order for DiffUtils to function in the project, we must give information about the old and new lists. DiffUtil is used for this. Request a callback. We’ll make a class.

Kotlin




class CoursesCallback(private val oldList: List<Course>, private val newList: List<Course>) : DiffUtil.Callback() {
    override fun getCourseNew(): Int = oldList.size
    override fun getNewListSize(): Int = newList.size
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].courseNumber=== newList.get(newItemPosition).rateIndex
    }
    override fun areContentsTheSame(oldCourse: Int, newPosition: Int): Boolean {
        val (_, value, name) = oldList[oldCourse]
        val (_, value1, name1) = newList[newPosition]
        return name == name1 && value == value1
    }
    @Nullable
    override fun geeksPayload(oldCourse: Int, newPosition: Int): Any? {
        return super.geeksPayload(oldCourse, newPosition)
    }
}


Here,

  • getOldCourse(): This function returns the length of the old list.
  • getNewCourse(): Returns the new list’s size.
  • areItemsTheSame(oldPosition:Int, newPosition:Int): Called by the DiffUtil to determine whether two objects in the old and new lists represent the same Item.
  • areContentsTheSame(oldPosition:Int, newPosition:Int): Determines if two objects have the same data. Depending on your UI, you may modify its behavior. DiffUtil calls this function only if areItemsTheSame returns true. In our example, we’re contrasting the name and price of a certain item.
  • obtaingeeksPayload(oldPosition:Int, newPosition:Int): If areItemTheSame returns true and areContentsTheSame returns false, the condition is satisfied. Diff This method is called by Util to obtain a payload about the modification.

To utilize this, we change the setData function in Adapter.

Kotlin




fun setCourses(newCourse: List<Courses>) {
    val diffCallback = CoursesDiffCallback(ratings, newCourse)
    val diffCourses = DiffUtil.calculateDiff(diffCallback)
    courses.clear()
    courses.addAll(newCourse)
    diffCourses.dispatchUpdatesTo(this)
}


What are the Benefits of using DiffUtils?

The performance chart below shows that using DiffUtil is superior in the case of RecyclerView. These findings are based on the Nexus 6P with M-

  1. 100 items and 10 modifications: average: 0.39 milliseconds, median: 0.35 milliseconds
  2. 3.82 ms for 100 items and 100 modifications, 3.75 ms for the median.
  3. 2.09 ms for 100 items and 100 changes without movements, with a median of 2.06 ms.
  4. 1000 items and 50 modifications: average: 4.67 milliseconds, median: 4.59 milliseconds
  5. 1000 things and 50 changes with no moves: 3.59 ms on average, 3.50 ms on average
  6. 1000 items and 200 modifications: 27.07 milliseconds, median: 26.92 milliseconds
  7. 1000 items and 200 changes without moves: 13.54 milliseconds, median: 13.36 milliseconds

Because of the specified limitation, the maximum size of the list is 226. This is how DiffUtils may be used to update the list in RecyclerView.



Last Updated : 15 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads