i am working on an online shopping application using retrofit, coroutine, livedata, mvvm,...
i want to show progress bar before fetching data from server for afew seconds
if i have one api request i can show that but in this app i have multiple request
what should i do in this situation how i should show progress bar??
Api Service
@GET("homeslider.php")
suspend fun getSliderImages(): Response<List<Model.Slider>>
@GET("amazingoffer.php")
suspend fun getAmazingProduct(): Response<List<Model.AmazingProduct>>
@GET("handsImages.php")
suspend fun getHandsFreeData(
@Query(
"handsfree_id"
) handsfree_id: Int
): Response<List<Model.HandsFreeImages>>
@GET("handsfreemoreinfo.php")
suspend fun gethandsfreemoreinfo(): Response<List<Model.HandsFreeMore>>
@GET("wristmetadata.php")
suspend fun getWristWatchMetaData(
@Query(
"wrist_id"
) wrist_id: Int
): Response<List<Model.WristWatch>>
repository
fun getSliderImages(): LiveData<List<Model.Slider>> {
val data = MutableLiveData<List<Model.Slider>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getSliderImages()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
fun getAmazingOffer(): LiveData<List<Model.AmazingProduct>> {
val data = MutableLiveData<List<Model.AmazingProduct>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getAmazingProduct()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
fun getHandsFreeData(handsree_id: Int): LiveData<List<Model.HandsFreeImages>> {
val dfData = MutableLiveData<List<Model.HandsFreeImages>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getHandsFreeData(handsree_id)
withContext(Main + SupervisorJob(job)) {
dfData.value = response.body()
}
job.complete()
job.cancel()
}
return dfData
}
fun getHandsFreeMore(): LiveData<List<Model.HandsFreeMore>> {
val data = MutableLiveData<List<Model.HandsFreeMore>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.gethandsfreemoreinfo()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
VIEWMODEL
fun getSliderImages() = repository.getSliderImages()
fun getAmazingOffer() = repository.getAmazingOffer()
fun recieveAdvertise() = repository.recieveAdvertise()
fun dailyShoes(context: Context) = repository.getDailyShoes(context)
i will appreciate your help
I couldn't help but notice that your repository contains lots of repetitive code. first point to learn here is that all that logic in Repository
, it usually goes in the ViewModel
. second thing is that you are using applicationScope
to launch your coroutines
, which usually is done using viewModelScope
(takes care of cancellation) object which is available in every viewModel
.
So first we have to take care of that repetitive code and move it to ViewModel
. So your viewModel would now look like
class YourViewModel: ViewModel() {
// Your other init code, repo creation etc
// Live data objects for progressBar and error, we will observe these in Fragment/Activity
val showProgress: MutableLiveData<Boolean> = MutableLiveData()
val errorMessage: MutableLiveData<String> = MutableLiveData()
/**
* A Generic api caller, which updates the given live data object with the api result
* and internally takes care of progress bar visibility. */
private fun <T> callApiAndPost(liveData: MutableLiveData<T>,
apiCall: () -> Response<T> ) = viewModelScope.launch {
try{
showProgress.postValue(true) // Show prgress bar when api call is active
if(result.code() == 200) { liveData.postValue(result.body()) }
else{ errorMessage.postValue("Network call failed, try again") }
showProgress.postValue(false)
}
catch (e: Exception){
errorMessage.postValue("Network call failed, try again")
showProgress.postValue(false)
}
}
/******** Now all your API call methods should be called as *************/
// First declare the live data object which will contain the api result
val sliderData: MutableLiveData<List<Model.Slider>> = MutableLiveData()
// Now call the API as
fun getSliderImages() = callApiAndPost(sliderData) {
repository.getSliderImages()
}
}
After that remove all the logic from Repository
and make it simply call the network methods as
suspend fun getSliderImages() = api.getSliderImages() // simply delegate to network layer
And finally to display the progress bar , simply observe the showProgress
LiveData
object in your Activity
/ Fragment
as
viewModel.showProgress.observer(this, Observer{
progressBar.visibility = if(it) View.VISIBLE else View.GONE
}
First create a enum class status:
enum class Status {
SUCCESS,
ERROR,
LOADING
}
Then create resource class like this:
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
Now add your request to a list of response:
var list = java.util.ArrayList<Response<*>>()
suspend fun getApis() = list.addAll(
listOf(
api.advertise(),
api.getAmazingProduct(),
api.dailyShoes(),
api.getSliderImages(),
.
.
.
)
)
In your viewmodel class:
private val _apis = MutableLiveData<Resource<*>>()
val apis: LiveData<Resource<*>>
get() = _apis
init {
getAllApi()
}
fun getAllApi() {
val job = Job()
viewModelScope.launch(IO + job) {
_apis.postValue(
Resource.loading(null)
)
delay(2000)
repository.getApis().let {
withContext(Main + SupervisorJob(job)) {
it.let {
if (it) {
_apis.postValue(Resource.success(it))
} else {
_apis.postValue(Resource.error("Unknown error eccured", null))
}
}
}
}
job.complete()
job.cancel()
}
}
Now you can use status to show progress like this . use this part in your target fragment:
private fun setProgress() {
viewModel.apis.observe(viewLifecycleOwner) {
when (it.status) {
Status.SUCCESS -> {
binding.apply {
progress.visibility = View.INVISIBLE
line1.visibility = View.VISIBLE
parentscroll.visibility = View.VISIBLE
}
}
Status.ERROR -> {
binding.apply {
progress.visibility = View.INVISIBLE
line1.visibility = View.INVISIBLE
parentscroll.visibility = View.INVISIBLE
}
}
Status.LOADING -> {
binding.apply {
progress.visibility = View.VISIBLE
line1.visibility = View.INVISIBLE
parentscroll.visibility = View.INVISIBLE
}
}
}
}
}
I hope you find it useful.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.