0

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.

  • Have you tried avoiding calling `chain.proceed(chain.request());` by throwing exception instead. That should work, as I understand, its going to your next interceptor in the chain because you are asking to chain.proceed, even after cancelling – vizsatiz Mar 07 '22 at 07:14
  • @vizsatiz First of all, thanks for the comment and your time Viz. Before this I tried with "by throwing exception" only. And it is throwing exception in next interceptor class where "return" statement is written. Now from there how will I handle? I tried to catch exception there also, but then how I should pass result to UI? –  Mar 07 '22 at 09:00
  • @vizsatiz please post the answer if u know the solution –  Mar 07 '22 at 09:56
  • I'd argue that what you want to do is "not correct from a separation of concerns point of view". Notice I used quotes. It's not retrofit's responsibility to deal with a network issue. It is delegated. Retrofit has a lot to do already and its job is clearly split into the part that it delegates to the Http stack (OkHttp). You should check for connectivity before making the request. If the network fails during, you gracefully fail and let the responsible entity handle that and retry or do whatever you consider appropriate. Don't fight the architecture provided by the tools you use. – Martin Marconcini Mar 08 '22 at 11:04

1 Answers1

1

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.

See https://square.github.io/okhttp//features/interceptors/#choosing-between-application-and-network-interceptors

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,

Something like https://github.com/Petrulak/android-kotlin-mvp-clean-architecture/blob/master/app/src/main/java/com/petrulak/cleankotlin/platform/connectivity/ConnectivityChecker.kt

or use a library like

https://github.com/erikhuizinga/flomo

Yuri Schimke
  • 12,435
  • 3
  • 35
  • 69
  • Hi Yuri, sorry to say but you didn't get my question. Let me explain you. So basically you answered that how to check Internet connection, but my question is slight different. I also want to implement connection check only, but I want to do according to my use case. The problem I'm facing is inside the AuthInterceptor class. From there how will I proceed further that I'm not getting it. Please re-read my question and request you to update your answer. –  Mar 07 '22 at 11:33
  • I already written connectivity helper class written in my project, but for sake of testing purpose, I manually set bool value, so that everytime it will go to else condition and then I want to see that how will I handle things in retrofit. –  Mar 07 '22 at 11:35
  • Then use that flag in an application Interceptor and throw an IOException before any connection is attempted. : let's say I used flag and it came to AuthInterceptor, there it is throwing the IOexception, now how will I prevent crash and same IOException I can return to retrofit callbacks? And also can't we do something that it won't even go to other interceptors –  Mar 07 '22 at 11:38
  • OK, but the key part if you want to stop a request from proceeding is that it needs to be an application interceptor. The network interceptors happen after it has established a connection. – Yuri Schimke Mar 07 '22 at 14:13
  • Hi Yuri, yes I put logs and verified that ApplicationInterceptors are called first and then network one. I changed from .addNetworkInterceptor to addkInterceptor and tried, but still problem is persists na! I'm not able to get callback in UI. –  Mar 07 '22 at 18:26