简体   繁体   中英

show progress bar before fetching data from server using retrofit

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM