Agung
Agung

Reputation: 13883

How to perform an action after all networking requests finished or one of them failed?

I have 4 API URL endpoints, in my main activity when the activity show, I will make 4 requests to each endpoint simultaneously. I want to make my request indicator (progress bar) to gone if all that 4 requests is finished successfully or if one of them failed with request code 4xx or 5xx.

So basically I want to perform some action after all requests finish or one of them failed?

In iOS development, I can easily use DispatchGroup to group some actions and do something after some condition. What is the common way to solve this in Android?

here is the code I use, for simplification, let say I have 2 method to make requests to the server. I am using retrofit:

first, to get products:

fun getProductsFromServer(customerID: String, type: String = "", categoryID: String = "" , completion: (errorMessage: String?, products: ArrayList<Product>?) -> Unit) {

            val lakuinAPI = RetrofitHandler.getInstance(LakuinAPI::class.java)
            val call = lakuinAPI.getProductData("1","0","0","100000000000",type = type,customer_id = customerID,categories_id = categoryID)

            call.enqueue(object: Callback<ProductData> {

                override fun onFailure(call: Call<ProductData>, t: Throwable) {
                    completion("Failed to make netwroking call : ${t.localizedMessage}", null)
                }

                override fun onResponse(call: Call<ProductData>, response: Response<ProductData>) {

                    val productList = ArrayList<Product>()

                    if (!response.isSuccessful) {
                        completion("Error: ${response.code()}",productList)
                        return
                    }

                    val jsonProductData = response.body() ?: return
                    val statusSuccess = jsonProductData.success

                    if (statusSuccess == "1") {

                        val products = jsonProductData.product_data
                        completion(null,products)

                    } else if (statusSuccess == "0") {

                        val errorMessageFromServer = jsonProductData.message
                        completion(errorMessageFromServer,productList)


                    }


                }


            })
        }

second, to get banners

fun getBannersFromServer(completion: (errorMessage: String?, banners: ArrayList<Banner>?) -> Unit) {

            val lakuinAPI = RetrofitHandler.getInstance(LakuinAPI::class.java)
            val call = lakuinAPI.getBanners()

            call.enqueue(object : Callback<BannerData> {

                override fun onFailure(call: Call<BannerData>, t: Throwable) {
                    completion("Failed to make netwroking call : ${t.localizedMessage}", null)
                }

                override fun onResponse(call: Call<BannerData>, response: Response<BannerData>) {

                    val bannerList = ArrayList<Banner>()

                    if (!response.isSuccessful()) {
                        completion("Error: ${response.code()}",bannerList)
                        return
                    }

                    val jsonBannerData = response.body() ?: return
                    val statusSuccess = jsonBannerData.success

                    if (statusSuccess == "1") {

                        val banners = jsonBannerData.data
                        completion(null,banners)

                    } else if (statusSuccess == "0") {
                        val errorMessageFromServer = jsonBannerData.message
                        completion(errorMessageFromServer,bannerList)


                    }

                }


            })




        }

and then those two methods will be used in Main Activity like this

private fun getProducts(type: String) {

        Product.getProductsFromServer(customerID = userData.id.toString(), type = type) { errorMessage, products ->

            errorMessage?.let {
                activity?.toast(it)
            } ?: run {

                val productList = products ?: ArrayList()
                setUpRecyclerView(type = type,products = productList)

            }


        }

    }


    private fun getBanners() {


        Banner.getBannersFromServer { errorMessage, banners ->

            errorMessage?.let {
                activity?.toast(it)
            } ?: run {
                val bannerList = banners ?: ArrayList()
                setUpImageSlideShow(banners = bannerList)
            }


        }
    }

so, after calling getBanners() and getProducts() , I want to hide progress bar, when those 2 request is successful. if failed let say I want to show toast massage ? how to do that ?

java is ok

Upvotes: 2

Views: 2359

Answers (3)

kamyar haqqani
kamyar haqqani

Reputation: 790

I'm not familiar with Retrofit but this should work:

create a publicly accessible array; after the completion or failure of each request add the status to this array;

override fun onFailure(call: Call<BannerData>, t: Throwable) {
                completion("Failed to make netwroking call : ${t.localizedMessage}", null)
                the_public_array.add("failure");
}
override fun onResponse(call: Call<BannerData>, response: Response<BannerData>) {
                val bannerList = ArrayList<Banner>()
                if (!response.isSuccessful()) {
                    the_public_array.add("failure")
                    completion("Error: ${response.code()}",bannerList)
                    return
                }
                val jsonBannerData = response.body() ?: return
                val statusSuccess = jsonBannerData.success
                if (statusSuccess == "1") {
                    the_public_array.add("success")
                    val banners = jsonBannerData.data
                    completion(null,banners)
                } else if (statusSuccess == "0") {
                    the_public_array.add("failure")
                    val errorMessageFromServer = jsonBannerData.message
                    completion(errorMessageFromServer,bannerList)
                }

            }

create a thread(in your activity) which runs a code that check that array all the time until it either contains one failed connection or 4 successful connections; then set the visiblity of your view to gone if this condition is met( in the ui thread);

Thread(Runnable {
    while(true){
        if(the_public_array.contains_four_success_status()||
                the_public_array.contains_one_failure_status()){
            [email protected](java.lang.Runnable { 
                the_progress_bar.set_Visibility(Gone) 
            }) 
            break;
         }
    }
}).start();

Upvotes: 0

Belal Khan
Belal Khan

Reputation: 2119

You can easily achieve this with Retrofit and Coroutines.

Step 1 Create Retrofit Calls

interface MyApi{

    @GET("end-point}")
    suspend fun firstCall(): Response<YourModelClassForResponse>

    @GET("end-point}")
    fun secondCall(): Response<YourModelClassForResponse>

    @GET("end-point}")
    fun thirdCall(): Response<YourModelClassForResponse>

    @GET("end-point}")
    fun fourthCall(): Call<YourModelClassForResponse>


    companion object {
        private const val BASE_URL = "bas url"
        operator fun invoke(): SmartFarmApi {
            return Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(BASE_URL)
                .build().create(MyApi::class.java)
        }
    }
}

Step 2 Call your APIs with Coroutines

    GlobalScope.launch { 
      val firstCall =  MyApi().firstCall()
      val secondCall=  MyApi().secondCall()
      val thirdCall=  MyApi().thirdCall()
      val fourthCall =  MyApi().fourthCall()
    }

Though using GlobalScope is not good idea, but just to make it quick I written it here, you can create the coroutine scope to make these calls. Again calling 4 different APIs parallel in a single activity or fragment is I don't consider as a good idea. I think you should change your API and make everything in one call.

Hope this helps. Thank You

Upvotes: 1

SaadAAkash
SaadAAkash

Reputation: 3193

For achieving this, using Anko is the preferred way if you're using Kotlin for Android Development.

doAsync {
    // ...
}

You can achieve the same with AsyncTask too, and plus it's an Android API, not a language feature of Java or Kotlin. Use AsyncTask like this:

class someTask() : AsyncTask<Void, Void, String>() {
    override fun doInBackground(vararg params: Void?): String? {
        // ...
    }

    override fun onPreExecute() {
        super.onPreExecute()
        // ...
    }

    override fun onProgressUpdate(vararg text: String?) {
        super.onPostExecute(result)
        // ...
    }

    override fun onPostExecute(result: String?) {
        super.onPostExecute(result)
        // ...
    }
}

Please follow this link to see how the task responds as the asynchronous task is being executed.

Upvotes: 1

Related Questions