Flow: I want to check Internet connection when Retrofit makes API call, and if there is no internet connection, then I don't want to proceed further and my request should cancel. Please very clearly note that, I saw many many post related to this, but most of are doing when retrofit is completing the request and it is going in onFailure, there people are handling Internet connection check. I do not want that. I want to make architecture for all my api call in a single place where my all api's call will check Internet connection first then only it will proceed, otherwise it will return and it should go simply in onfailure.
Let me explain my problem with code:
This is my OkHttp functionality which I provide through Hilt:
......
okHttpClientBuilder.addNetworkInterceptor(NetworkInterceptor())
okHttpClientBuilder.addInterceptor(logger)
okHttpClientBuilder.authenticator(refreshTokenAuthenticator)
okHttpClientBuilder.addInterceptor(AuthInterceptor(context, tokenProvider))
....
Point to be noted that, I used .addNetworkInterceptor for my NetworkInterceptor class, and for other Interceptors, like AuthInterceptor, where I'm appending headers, there I used .addInterceptor
NetworkInterceptor
class NetworkInterceptor: Interceptor {
//TODO: temp hardcoded to test the functionality
private val isNetworkActive = false
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
if (isNetworkActive) {
return chain.proceed(request).newBuilder().build()
} else {
chain.call().cancel()
return chain.proceed(chain.request());
}
}
}
So, basically it is going to else condition, please correct my else condition if anything is wrong. Also people are throwing an exception from here, but that will not work in my case, because the thing is, after going from else condition, it is going to AuthInterceptor class. I tried to modified sequence / statement of above and down (like first auth and then network one..), but in all cases it is going to my AuthInterceptor. The one reason, I feel is because .addNetworkInterceptor called first and then .addInterceptor even though it is written in any sequence!
this sequence:
okHttpClientBuilder.addNetworkInterceptor(NetworkInterceptor())
okHttpClientBuilder.addInterceptor(logger)
okHttpClientBuilder.authenticator(refreshTokenAuthenticator)
okHttpClientBuilder.addInterceptor(AuthInterceptor(context, tokenProvider))
AuthInterceptor
class AuthInterceptor @Inject constructor(private val context: Context, private val tokenProvider: ILocalTokenProvider) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
//TODO remove context also from constructor
requestBuilder.header(AppConstants.INetworkValues.AUTHORIZATION, tokenProvider.getAccessToken() ?: "")
return chain.proceed(requestBuilder.build())
}
}
So, if you see my return statement there app is crashing with this: java.io.IOException: Canceled
Also what I want is that if I'm cancelling my req from network intercptor then I don't want to go in other interceptors, it should return and I should get failure in retrofit callback.
Any help would be really appreciated, like I'm trying this basic problem from last few days.
CodePudding user response:
The key part is to check the connectivity statusapplication Interceptor and throw an IOException before any connection is attempted. network interceptors must proceed exactly once.
Then make sure you handle that exception gracefully in the caller.
Or return a dummy or cached result when network is not available.
As you are already doing, you need to either hook into the ConnectivityManager yourself,
or use a library like
https://github.com/erikhuizinga/flomo
CodePudding user response:
This is the correct answer, basically we should throw an Exception from Interceptors, and whatever is the next "response-receiving layer" for example activity, fragment, repository, viewmodel, remotedatasource. In my case next layer is remotedatasource, so I catch the exception there.
See this answer: