How to Implement Offline Caching using NetworkBoundResource in Android?
Last Updated :
11 Apr, 2023
Almost, every android application that requires fetching data over a network, needs caching. First, let understand What does caching means? Most of us have used applications that require the data to be fetched from the web. Such an application with an offline-first architecture will always try to fetch the data from the local storage. On the other hand, if there is some failure, it requests the data to be fetched from a network, thereafter storing it locally, for future retrieval. The data will be stored in an SQLite database. The advantage of such an architecture is that we will be able to use the application even if it is offline. Moreover, since the data is cached, the application will respond faster. To handle caching, we will be using NetworkBound Resource. It is a helper class that decides when to use the cache data and when to fetch data from the web and update the View. It coordinates between the two.

The above decision tree shows the algorithm for the NetworkBound Resource algorithm.
The Algorithm
Let us see the flow of this algorithm:
- Whenever the user accesses the application in offline mode, the data is dispatched into the view, it can either be a fragment or an activity.
- If there is no data or the data is insufficient in the disk as a cache, then it should fetch the data over the network.
- It checks if there is a need to log in (if the user logouts, then re-login would be required). It re-authenticates, if successful then it fetches the data, but it failed, then it prompts the user to re-authenticate.
- Once the credentials are matched, then it fetches the data over the network.
- If the fetch phase is failed, then it prompts the user.
- Otherwise, if successful, then the data is stored automatically into the local storage. It then refreshes the view.
The requirement here is, there should be minimal changes in the User Experience when the user comes to online mode. So process like Re-authentication, fetching data over the network, and refreshing the views should be done in the background. One thing to be noted here is, the user only needs to re-login, if there are some changes in the user credentials like password, or username.
Implementation
To understand more about this, let us build an application. This is a simple news application, which uses a fake API for fetching data from the web. Let us look at the high-level design of our application:
- It will be using MVVM architecture.
- SQLite database for caching data.
- Use Kotlin FLow.(Kotlin Coroutine)
- Dagger Hilt for dependency injection.

The above diagram is the overview of the architecture that will be implemented in our application. This architecture is recommended by Android to develop a modern well-architecture android application. Let us start building the project.
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: Setting up the layout
It is always recommended to first set up the layout, followed by implementing the logic. So we will first create the layout. As mentioned, we will be fetching data from a web service. Since this is a sample project, we would just fetch data from a random data generator. Now the data is a list of cars, which would include the following properties:
- Make and model of car
- Transmission of the car
- Colour of the car
- Drive type of the car.
- Fuel type of the car.
- Car type of the car.
We will be using RecyclerView to show the list. Hence first it is required to design how each element of the list would look like. Followed by making the list.
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp">
<!-- This will display the make and model of the car-->
<TextView
android:id="@+id/car_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="@color/black"
android:textSize="15sp"
tools:text="Car Name" />
<!-- This will display the transmission type of the car-->
<TextView
android:id="@+id/car_transmission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@id/car_name"
tools:text="Transmission type" />
<!-- This will display the colour of the car-->
<TextView
android:id="@+id/car_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/car_name"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
tools:text="Car colour" />
<!-- This will display the drive type of the car-->
<TextView
android:id="@+id/car_drive_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/car_name"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_toEndOf="@id/car_color"
tools:text="Car Drive Type" />
<!-- This will display the fuel type of the car-->
<TextView
android:id="@+id/car_fuel_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/car_transmission"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_toEndOf="@id/car_drive_type"
tools:text="Car fuel_type" />
<!-- This will display the car type of the car-->
<TextView
android:id="@+id/car_car_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/car_transmission"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_toEndOf="@id/car_fuel_type"
tools:text="Car Type" />
</RelativeLayout>
Now, let's code the list layout:
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CarActivity">
<!-- The recycler view-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_viewer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="4dp"
tools:listitem="@layout/carlist_item" />
<!--Initially the app will fetch data from the
web, hence a progress bar for that-->
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="invisible"
tools:visibility="visible" />
<!--If the application is not able to
fetch/ expose the data to the view-->
<TextView
android:id="@+id/text_view_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="8dp"
android:gravity="center_horizontal"
android:visibility="invisible"
tools:text="Error Message"
tools:visibility="visible" />
</RelativeLayout>
Step 3: Now let's create the API package
CarListAPI.kt
Kotlin
package com.gfg.carlist.api
import com.gfg.carlist.data.CarList
import retrofit2.http.GET
interface CarListAPI {
// Companion object to hold the base URL
companion object{
const val BASE_URL = "https://random-data-api.com/api/"
}
// The number of cars can be varied using the size.
// By default it is kept at 20, but can be tweaked.
// @GET annotation to make a GET request.
@GET("vehicle/random_vehicle?size=20")
// Store the data in a list.
suspend fun getCarList() : List<CarList>
}
Step 4: Implementing the app module
A module is nothing but an object class, which provides a container to the app's source code. It encapsulates data models associated with a task. The android architecture suggests making minimal use of business logic in the view model, hence the business application task is represented in the app module. It will include three methods:
- A method for calling the API via Retrofit
- A method to provide the list
- A method to provide the database or rather build a database.
AppModule.kt
Kotlin
package com.gfg.carlist.di
import android.app.Application
import androidx.room.Room
import com.gfg.carlist.api.CarListAPI
import com.gfg.carlist.data.CarListDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit =
Retrofit.Builder()
.baseUrl(CarListAPI.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
@Provides
@Singleton
fun provideCarListAPI(retrofit: Retrofit): CarListAPI =
retrofit.create(CarListAPI::class.java)
@Provides
@Singleton
fun provideDatabase(app: Application): CarListDatabase =
Room.databaseBuilder(app, CarListDatabase::class.java, "carlist_database")
.build()
}
Step 5: Creating Data Class
We are done with handling the API, fetching the data from the web service, but where to store the data? Let's create a class to store the data. We have to create a data class. If the app were to just fetch and expose data, then it would have just a single data class file. But here, we have to fetch, expose as well as cache the data. Hence ROOM comes into play here. So in the data class, we've to create an entity.
CarList.kt
Kotlin
package com.gfg.carlist.data
import androidx.room.Entity
import androidx.room.PrimaryKey
// Data Class to store the data
// Here the name of the table is "cars"
@Entity(tableName = "cars")
data class CarList(
@PrimaryKey val make_and_model: String,
val color: String,
val transmission: String,
val drive_type: String,
val fuel_type: String,
val car_type: String
)
Since we would be caching the data locally, hence a database is needed to be created.
CarListDatabase.kt
Kotlin
package com.gfg.carlist.data
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [CarList::class], version = 1)
abstract class CarListDatabase : RoomDatabase() {
abstract fun carsDao(): CarsDao
}
Since we have created a table, we need to have some queries to retrieve data from the table. This is achieved using DAO or Data Access Object.
CarsDao.kt
Kotlin
package com.gfg.carlist.data
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao
interface CarsDao {
// Query to fetch all the data from the
// SQLite database
// No need of suspend method here
@Query("SELECT * FROM cars")
// Kotlin flow is an asynchronous stream of values
fun getAllCars(): Flow<List<CarList>>
// If a new data is inserted with same primary key
// It will get replaced by the previous one
// This ensures that there is always a latest
// data in the database
@Insert(onConflict = OnConflictStrategy.REPLACE)
// The fetching of data should NOT be done on the
// Main thread. Hence coroutine is used
// If it is executing on one one thread, it may suspend
// its execution there, and resume in another one
suspend fun insertCars(cars: List<CarList>)
// Once the device comes online, the cached data
// need to be replaced, i.e. delete it
// Again it will use coroutine to achieve this task
@Query("DELETE FROM cars")
suspend fun deleteAllCars()
}
A repository class to handle data from web service and the data locally.
CarListRepository.kt
Kotlin
package com.gfg.carlist.data
import androidx.room.withTransaction
import com.gfg.carlist.api.CarListAPI
import com.gfg.carlist.util.networkBoundResource
import kotlinx.coroutines.delay
import javax.inject.Inject
class CarListRepository @Inject constructor(
private val api: CarListAPI,
private val db: CarListDatabase
) {
private val carsDao = db.carsDao()
fun getCars() = networkBoundResource(
// Query to return the list of all cars
query = {
carsDao.getAllCars()
},
// Just for testing purpose,
// a delay of 2 second is set.
fetch = {
delay(2000)
api.getCarList()
},
// Save the results in the table.
// If data exists, then delete it
// and then store.
saveFetchResult = { CarList ->
db.withTransaction {
carsDao.deleteAllCars()
carsDao.insertCars(CarList)
}
}
)
}
Step 6: Working on the UI
Remember in Step 1, we created a RecyclerView to expose the list of cars. But the work is not completed till now. We need to make an adapter as well as a ViewModel. These two classes work together to define how our data is displayed.
CarAdapter.kt
Kotlin
package com.gfg.carlist.features.carlist
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.gfg.carlist.data.CarList
import com.gfg.carlist.databinding.CarlistItemBinding
class CarAdapter : ListAdapter<CarList, CarAdapter.CarViewHolder>(CarListComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CarViewHolder {
val binding =
CarlistItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CarViewHolder(binding)
}
override fun onBindViewHolder(holder: CarViewHolder, position: Int) {
val currentItem = getItem(position)
if (currentItem != null) {
holder.bind(currentItem)
}
}
// View Holder class to hold the view
class CarViewHolder(private val binding: CarlistItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(carlist: CarList) {
binding.apply {
carName.text = carlist.make_and_model
carTransmission.text = carlist.transmission
carColor.text = carlist.color
carDriveType.text = carlist.drive_type
carFuelType.text = carlist.fuel_type
carCarType.text = carlist.car_type
}
}
}
// Comparator class to check for the changes made.
// If there are no changes then no need to do anything.
class CarListComparator : DiffUtil.ItemCallback<CarList>() {
override fun areItemsTheSame(oldItem: CarList, newItem: CarList) =
oldItem.make_and_model == newItem.make_and_model
override fun areContentsTheSame(oldItem: CarList, newItem: CarList) =
oldItem == newItem
}
}
CarListViewModel.kt
Kotlin
package com.gfg.carlist.features.carlist
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.gfg.carlist.data.CarListRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
// Using Dagger Hilt library to
// inject the data into the view model
@HiltViewModel
class CarListViewModel @Inject constructor(
repository: CarListRepository
) : ViewModel() {
val cars = repository.getCars().asLiveData()
}
Finally, we have to create an activity to show the data from the ViewModel. Remember, all the business logic should be present in the ViewModel, and not in the activity. The activity should also not hold the data, because when the screen is tilted, the data gets destroyed, due to which the loading time increases. Hence the purpose of the activity is to only show the data.
CarActivity.kt
Kotlin
package com.gfg.carlist.features.carlist
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.gfg.carlist.databinding.ActivityCarBinding
import com.gfg.carlist.util.Resource
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class CarActivity : AppCompatActivity() {
// Helps to preserve the view
// If the app is closed, then after
// reopening it the app will open
// in a state in which it was closed
// DaggerHilt will inject the view-model for us
private val viewModel: CarListViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// The below segment would
// instantiate the activity_car layout
// and will create a property for different
// views inside it!
val binding = ActivityCarBinding.inflate(layoutInflater)
setContentView(binding.root)
val carAdapter = CarAdapter()
binding.apply {
recyclerViewer.apply {
adapter = carAdapter
layoutManager = LinearLayoutManager(this@CarActivity)
}
viewModel.cars.observe(this@CarActivity) { result ->
carAdapter.submitList(result.data)
progressBar.isVisible = result is Resource.Loading<*> && result.data.isNullOrEmpty()
textViewError.isVisible = result is Resource.Error<*> && result.data.isNullOrEmpty()
textViewError.text = result.error?.localizedMessage
}
}
}
}
Finally, we are done with the coding part. After successfully building the project, the app would look like this:
Output:

The following video demonstrates the application.
Output Explanation:
Project Link: Click Here
Similar Reads
Implement Caching in Android Using RxJava Operators
The cache on your Android phone is a collection of little pieces of information that your apps and web browser utilize to improve efficiency. RxOperator is essentially a function that specifies the observable, as well as how and when it should emit the data stream. In RxJava, there are hundreds of o
5 min read
How to Implement Pagination in Android RecyclerView using Volley?
Pagination is one of the most important factors which helps to reduce the loading time inside our app and increase the performance of our data which is represented in the form of Lists. In this article, we will take a look at adding pagination in our Android RecyclerView. And to get the data from AP
11 min read
How to Implement Swipe Down to Refresh in Android
Certain applications show real-time data to the users, such as stock prices, availability of a product on online stores, etc. Showing real-time data requires continuous syncing of the application, and could be possible by implementing a program such as a thread. A Thread could be initiated by the ap
3 min read
How to Get the MAC of an Android Device using NetworkInterface Class?
The MAC (Media Access Control) address in Android is a unique identifier assigned to the Wi-Fi module of the device. It is used to identify devices on a network and helps in network communication and security. The general method for getting the MAC address of the android device is using the Wifi Man
3 min read
How to Implement Paging Library in Android with Example?
As the number of users of a particular mobile application grows, so does the amount of data associated with that application. For example, as the number of Instagram app users increased, so did the number of daily feeds on the app. In general, if we want to display data from a remote server in our A
11 min read
How to Update Data in API using Retrofit in Android?
We have seen reading data from API as well as posting data to our database with the help of the API. In this article, we will take a look at updating our data in our API. We will be using the Retrofit library for updating our data in our API. What we are going to build in this article? We will be
6 min read
How to Implement OnSavedInstanceState in Android?
In android, Preserving and restoring an activity's UI state in a timely fashion across system-initiated activity or application destruction is a crucial part of the user experience. In these cases the user expects the UI state to remain the same, but the system destroys the activity and any state st
4 min read
Implement Universal Image Loader Library in Android using Kotlin
Universal Image Loader library is also referred to as (UIL). It is similar to Picasso and Glide which is used to load the images from the URL within our image view inside our android application. This image loading library has been created to provide a powerful, flexible, and customizable solution t
4 min read
Implement Android Pull-to-Refresh with ListVIew using Kotlin
Swipe to Refresh Layout is used in most social media applications such as Facebook or Instagram. In these applications, users can simply swipe down to refresh the posts or feeds within the applications to load the new ones. Swipe to refresh layout detects vertical swipe and displays a progress bar.
7 min read
How to Post Data to API using Retrofit in Android?
We have seen reading data from API in our Android app in Android Studio. For reading data from API, we use GET request to read our data which is in JSON format. In this article, we will take a look at adding data to REST API in our Android App in Android Studio. What we are going to build in this ar
6 min read