Open In App

How to Improve RecyclerView Performance in Android Using DiffUtil?

Last Updated : 23 Mar, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

DiffUtil class is used to improve the performance of RecyclerView. The algorithm behind DiffUtil is that it takes two lists and finds differences between the two and provides the updated list as an output. It uses Eugene W. Myers’s difference algorithm to calculate the minimal number of updates. In this article, we will see its implementations. Note that we are going to implement this project using the Kotlin language. 

Step by Step Implementation

Step 1: Create a new project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.

Step 2: Add view binding dependency

Go to build.gradle(app) and the following dependency inside the android tag and click sync now.

 buildFeatures {

      viewBinding true

  }

Step 3: Create a new model class

Create a new class Language.kt we will use data of custom generic “Language” to pass in the list that will be shown in RecyclerView.

Kotlin




// this is the Language model class
class Language(
    val id : Int =0,
    val name : String ="",
    val exp : String =""
)


Step 4: Create a new layout file and name it the single_item.xml file

Go to the single_item.xml file and refer to the following code. Below is the code for the single_item.xml file. It is the single item layout that we will use in RecyclerView.

XML




<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 
    android:layout_width="match_parent"
    android:layout_marginBottom="10dp"
    android:layout_marginStart="5dp"
    android:layout_marginEnd="5dp"
    android:layout_height="110dp">
  
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
          
        <!--Add image view, We will not set any data here.-->
        <ImageView
            android:id="@+id/iv_language"
            android:layout_width="80dp"
            android:layout_height="90dp"
            android:layout_marginStart="20dp"
            android:layout_marginTop="10dp"
            android:scaleType="fitCenter"
            android:src="@mipmap/ic_launcher"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
          
        <!--Text view for showing the language name-->
        <TextView
            android:id="@+id/tv_lang_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="30dp"
            android:layout_marginTop="30dp"
            android:text="Language"
            android:textSize="16sp"
            android:textStyle="bold"
            app:layout_constraintLeft_toRightOf="@id/iv_language"
            app:layout_constraintTop_toTopOf="parent" />
  
        <!--Text View for showing the exp-->
        <TextView
            android:id="@+id/tv_exp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="Exp : 3 years"
            app:layout_constraintLeft_toLeftOf="@id/tv_lang_name"
            app:layout_constraintTop_toBottomOf="@id/tv_lang_name" />
  
    </androidx.constraintlayout.widget.ConstraintLayout>
  
</com.google.android.material.card.MaterialCardView>


Step 5: Working with the activity_main.xml

Go to the activity_main.xml file and refer to the following code. Below is the code for the activity_main.xml file. It has only a single RecyclerView which we will use to show our data.

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:background="#F5F8FD"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <!--Add recycler view to main activity-->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:listitem="@layout/single_item"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
  
    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/btn_new_language"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add New Language"
        android:padding="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginBottom="20dp"/>
    
</androidx.constraintlayout.widget.ConstraintLayout>


Step 6: Create a new class and name it MyDiffUtil.kt

Go to MyDiffUtil.kt file and write the following code. Comments are added for a better understanding of the code.

Kotlin




import androidx.recyclerview.widget.DiffUtil
  
// pass two list one oldList and second newList
class MyDiffUtil(
    private val oldList : List<Language>,
    private val newList : List<Language>
) :DiffUtil.Callback() {
    // implement methods
    override fun getOldListSize(): Int {
        // return oldList size
        return oldList.size
    }
  
    override fun getNewListSize(): Int {
        // return newList size
        return newList.size
    }
  
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        // compare items based on their unique id
        return oldList[oldItemPosition].id == newList[newItemPosition].id
    }
  
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        // in here compare each item if they are same or different
        // return false if any data is same else return true
        return when{
            oldList[oldItemPosition].id != newList[newItemPosition].id -> false
            oldList[oldItemPosition].name != newList[newItemPosition].name -> false
            oldList[oldItemPosition].exp != newList[newItemPosition].exp -> false
            else -> true
        }
    }
}


Step 7: Working with the Adapter class

Create a new class RvAdapter.kt this will act as an Adapter class for the recycler view. Using View binding we use the generated class of the layout single_item.xml ie SingleItemBinding to add data and view in the recycler view of MainActivity.kt in our case. Comments are added before the code for better understanding.

Kotlin




import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.geeksforgeeks.rvadapterviewbinding.databinding.SingleItemBinding
  
class RvAdapter() : RecyclerView.Adapter<RvAdapter.ViewHolder>() {
  
    private var oldLanguageList= emptyList<Language>()
    // create an inner class with name ViewHolder
    // It takes a view argument, in which pass the generated class of single_item.xml
    // ie SingleItemBinding and in the RecyclerView.ViewHolder(binding.root) pass it like this
    inner class ViewHolder(val binding: SingleItemBinding) : RecyclerView.ViewHolder(binding.root)
  
    // inside the onCreateViewHolder inflate the view of SingleItemBinding
    // and return new ViewHolder object containing this layout
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = SingleItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }
  
    // bind the items with each item of the list languageList which than will be
    // shown in recycler view
    // to keep it simple we are not setting any image data to view
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        with(holder){
            with(oldLanguageList[position]){
                binding.tvLangName.text = this.name
                binding.tvExp.text = this.exp
            }
        }
    }
  
    // return the size of languageList
    override fun getItemCount(): Int {
        return oldLanguageList.size
    }
  
    fun setData(newLanguageList : List<Language>){
        val diffUtil = MyDiffUtil(oldLanguageList , newLanguageList)
        // it calculates the different items of the oldLanguageList and newLanguageList
        val diffResult = DiffUtil.calculateDiff(diffUtil)
        // assign oldLanguageList to newLanguageList
        oldLanguageList = newLanguageList
        diffResult.dispatchUpdatesTo(this)
    }
}


Step 8: Working with the MainActivity.kt file

Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.

Kotlin




import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.geeksforgeeks.rvadapterviewbinding.databinding.ActivityMainBinding
  
class MainActivity : AppCompatActivity() {
  
    // view binding for the activity
    private var _binding : ActivityMainBinding? = null
    private val binding get() = _binding!!
  
    // get reference to the adapter class
    private val rvAdapter by lazy { RvAdapter() }
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
  
        // define layout manager for the Recycler view
        binding.rvList.layoutManager = LinearLayoutManager(this)
          
        // attach adapter to the recycler view
        binding.rvList.adapter = rvAdapter
  
        // create new objects
        val language1= Language(1,"Java" , "Exp : 3 years")
        val language2=Language(2,"Kotlin" , "Exp : 2 years")
        val language3=Language(3,"Python" , "Exp : 4 years")
  
        // call set data method of adapter class and the list data
        rvAdapter.setData(listOf(language1, language2, language3))
        binding.btnNewLanguage.setOnClickListener {
            // on click of button add one more item to the list
            val language4= Language(4,"CPP" , "Exp : 5 years")
            rvAdapter.setData(listOf(language1,language2,language3,language4))
        }
    }
  
    // on destroy of view make the binding reference to null
    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads