Warning
This project is no more maintained. Expect some minor bugs/ compatibility issues
An android app to demonstrate offline caching capabilities offered by JetPack Libraries.
Link to Geeksforgeeks article.
The Jetpack article describes a way to provide data from a web service or retrieve data from an offline storage(if available). This repository contains the files required to make an application which can demonstrate such capabilities.
Before starting, lets see how our app would look:
The block diagram represents the control flow :
The goal here is to minimize the changes in the user-expereince. Important things to be noted:
- For the first time, when the application is started, the data is fetched from the webservice(restAPI, AWS, etc) in a background thread.
- As soon as the data fetch is done, all the data is stored in the local database, using the ROOM persistence library
- Parallelly, the data is exposed and shown in the activity/ fragment.
- If any change is there in the data, then the whole view is not refreshed, rather it is fetched in the background thread, laterwards the new data is replaced with the old data
The app follows the MVVM architecture.
The core part of this application is the NetworkBoundResource.kt
file, where the magic happens.
inline fun <ResultType, RequestType> networkBoundResource(
crossinline query: () -> Flow<ResultType>,
crossinline fetch: suspend () -> RequestType,
crossinline saveFetchResult: suspend (RequestType) -> Unit,
crossinline shouldFetch: (ResultType) -> Boolean = { true }
) = flow {
val data = query().first()
val flow = if (shouldFetch(data)) {
emit(Resource.Loading(data))
try {
saveFetchResult(fetch())
query().map { Resource.Success(it) }
} catch (throwable: Throwable) {
query().map { Resource.Error(throwable, it) }
}
} else {
query().map { Resource.Success(it) }
}
emitAll(flow)
}
- The above code first checks if there is any requirement for fetching the data or not. .
- If it is required to fetch the data, then it is emitted.
- Else just look into the map
- Kotlin Flow is used here
This repository contains code for an android application, which basically shows a list of data fetched from a random API generator, using RetrofitAPI
using which APIs are converted into callable objects.
The following data are required to be fetched and shown in the activity.
// Data Class to store the data
@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
)
A repository
will be used. Repository pattern is one of the design patterns that available out there.A Repository is defined as a collection of domain objects that reside in the memory.
DAO
for the API path
interface CarsDao {
@Query("SELECT * FROM cars")
fun getAllCars() : Flow<List<CarList>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCars(cars : List<CarList>)
@Query("DELETE FROM cars")
suspend fun deleteAllCars()
}
Repository
class to centralize the data access
class CarListRepository @Inject constructor(
private val api : CarListAPI,
private val db : CarListDatabase
) {
private val carsDao = db.carsDao()
fun getCars() = networkBoundResource(
query = {
carsDao.getAllCars()
},
fetch = {
delay(2000)
api.getCarList()
},
saveFetchResult = { CarList ->
db.withTransaction {
carsDao.deleteAllCars()
carsDao.insertCars(CarList)
}
}
)
}
This thing has to be implemented in a viewModel
from which data will be exposed on a view or a fragment.
The view model can get data from the repository by observing it's live data.
@HiltViewModel
class CarListViewModel @Inject constructor(
api : CarListAPI
) : ViewModel() {
private val carListLiveData = MutableLiveData<List<CarList>>()
val carList : LiveData<List<CarList>> = carListLiveData
init {
viewModelScope.launch {
val carList = api.getCarList()
delay(2000)
carListLiveData.value = carList
}
}
}
Adding a repository between the data source and a view is recommended by Android, as it seperates the view, so that focus can be put seperately on increasing the UI of app and the database. Moreover, the repository helps by centralising the data access, which directly reduces the boilerplate code.
The app uses MVVM Architecture
- Go to https://github.com/duttabhishek32/NetworkBound-OfflineCaching fork repo.(It's very easy, just follow the steps).
- Now click on the "*Issues*" and see which issues you can work on.
- Open the "*database.cpp*" file and then click on the edit button and then do the changes.
- Click on the "*Commit changes *", and then click on the "*Create Pull Request*" button.
- I will then reveiw the PR(Pull Request) and if I found it to be correct I will merge it in main.