Parallel request with Retrofit, Coroutines and Suspend functions any request 401 after we call refresh token, update access token then call the same request again,

class ServiceInterceptor() : Interceptor {
    fun getSharedPreferences(): SharedPreferences {
        return PreferenceHelper.customPrefs(TelioEVApp.appContext, "SHARED_DB")
    }
    private fun getAccessToken(): String? {
        val sharedPreferences = PreferenceHelper.customPrefs(TelioEVApp.appContext, "SHARED_DB")
        val token: String? = sharedPreferences.getString(PREF_ACCESS_TOKEN, "")
        return token?.trim() ?: ""
    }
    override fun intercept(chain: Interceptor.Chain): Response {
     val tokenManager = object : TokenManager {
            val sharedPreferences =
                PreferenceHelper.customPrefs(TelioEVApp.appContext, "SHARED_DB")

            override fun getToken(): String? = getAccessToken()

            override fun hasToken(): Boolean? {
                if (getToken().isNullOrBlank()) {
                    return false
                }
                return true
            }

            override fun clearToken() {

                sharedPreferences.setData(PREF_ACCESS_TOKEN, "")
            }

            override fun refreshToken(): String? {
                val refreshToken: String? =
                    sharedPreferences.getData(Constants.PREF_REFRESH_TOKEN, "")
                val client = RetrofitBuilder.getUnsafeOkHttpClient()
                val params = JSONObject()
                val body: RequestBody = RequestBody.create(
                    "application/json".toMediaTypeOrNull(),
                    params.toString()
                )
                val nRequest = Request.Builder()
                    .post(body)
                    .header("accept", "application/json")
                    .header("X-Device-ID", "ANDROID")
                    .header("Content-Type", "application/json-patch+json")
                    .header("refresh_token", "$refreshToken")
                    .url("${BuildConfig.BASE_URL}${BuildConfig.BASE_PORT}/api/token/refresh")
                    .build()
                val response = client?.newCall(nRequest)?.execute()
                if (response?.code == 200) {
                    // Get response
                    val jsonData = response.body?.string() ?: ""
                    val gson = Gson()
                    val loginResponse: ResponseBodies.LoginResponse = gson.fromJson(
                        jsonData,
                        ResponseBodies.LoginResponse::class.java
                    )
                    if (loginResponse.statusCode == 200) {
                        sharedPreferences.setData(
                            Constants.PREF_ACCESS_TOKEN,
                            loginResponse.body?.accessToken
                        )
                        sharedPreferences.setData(
                            Constants.PREF_REFRESH_TOKEN,
                            loginResponse.body?.refreshToken
                        )

                        sharedPreferences.setData(
                            Constants.PREF_EXPIRES_IN,
                            loginResponse.body?.expireIn
                        )
                        // Add refreshToken expires to shared preferences
                        sharedPreferences.setData(
                            Constants.PREF_REFRESH_TOKEN_EXPIRES,
                            loginResponse.body?.refreshExpireIn
                        )
                        // add user delta time to shared preferences
                        sharedPreferences.setData(
                            Constants.PREF_USER_DELTA_TIME,
                            System.currentTimeMillis() / 1000
                        )
                    } else {
                        val  shareViewModel = ViewModelProvider(TelioEVApp.getInstance()).get(SharedViewModel::class.java)
                        shareViewModel.onLogout(ShareDataBean(1,loginResponse.message?:"Session expired"))
                    }
                }
                return getAccessToken()

            }
        }
        val request = chain.request()
        val apiRequest = request.newBuilder()
            .addHeader("X-Device-ID", "ANDROID")
            .apply {
                if (!getAccessToken().isNullOrBlank())
                    header("Authorization", "Bearer " + getAccessToken())
            }
            .build()
//        request.header("Authorization:Bearer" + getAccessToken())
        val response = chain.proceed(apiRequest)
        var unauthorized = false
        if (response.code == 401 || response.code == 422) {
            response.close()
            unauthorized = true
        }

        if (unauthorized) {
            tokenManager.clearToken() // remove old access token
            tokenManager.refreshToken() // hit new api
            val accessToken : String? = tokenManager.getToken()
            if (!accessToken.isNullOrBlank()) {
                val modifiedRequest = request.newBuilder()
                    .addHeader("X-Device-ID", "ANDROID")
                    .addHeader("Authorization", "Bearer $accessToken")
                    .build()

                return chain.proceed(modifiedRequest)
            }
        }
        return response
    }

}

ServiceInterceptor.kt

    lifecycleScope.launch {
        try {
    
            itemIds.asFlow()
                   .flowOn(Dispatchers.IO) 
                   .collect{ itemId -> itemById[itemId] = MyService.getItem(itemId)}

itemIds.asFlow()
                   .flowOn(Dispatchers.IO) 
                   .collect{ itemId -> itemById[itemId] = MyService.getItem(itemId)}
    
        } catch (exception: Exception) {
            exception.printStackTrace()
        }
    
        Log.i(TAG, "All requests have been executed")
    }

Also please look at the lifecycle scope how to check refresh token call when any API 401 than call same request again

0

There are 0 best solutions below