okhttp: App crashes on throwing the exception from the Interceptor

There are the implementations i have been using

implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'

This is the network interceptor class

class NetworkConnectionInterceptor(
    context: Context
) : Interceptor {


    private val applicationContext = context.applicationContext

    override fun intercept(chain: Interceptor.Chain): Response {
        return if (!isConnectionOn()) {
            throw NoConnectivityException()
        } else if(!isInternetAvailable()) {
            throw NoInternetException()
        } else {
            chain.proceed(chain.request())
        }
    }

    private fun isInternetAvailable(): Boolean {
        return try {
            val timeoutMs = 1500
            val sock = Socket()
            val sockaddr = InetSocketAddress("8.8.8.8", 53)

            sock.connect(sockaddr, timeoutMs)
            sock.close()

            true
        } catch (e: IOException) {
            false
        }
    }

    private fun isConnectionOn(): Boolean {
        val connectivityManager =
            applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as
                    ConnectivityManager

        return if (android.os.Build.VERSION.SDK_INT >=
            android.os.Build.VERSION_CODES.M) {
            postAndroidMInternetCheck(connectivityManager)
        } else {
            preAndroidMInternetCheck(connectivityManager)
        }
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun postAndroidMInternetCheck(
        connectivityManager: ConnectivityManager): Boolean {
        val network = connectivityManager.activeNetwork
        val connection =
            connectivityManager.getNetworkCapabilities(network)

        return connection != null && (
                connection.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                        connection.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
    }

    private fun preAndroidMInternetCheck(
        connectivityManager: ConnectivityManager): Boolean {
        val activeNetwork = connectivityManager.activeNetworkInfo
        if (activeNetwork != null) {
            @Suppress("DEPRECATION")
            return (activeNetwork.type == ConnectivityManager.TYPE_WIFI ||
                    activeNetwork.type == ConnectivityManager.TYPE_MOBILE)
        }
        return false
    }

}

My Retrofit client

operator fun invoke(
            networkConnectionInterceptor: NetworkConnectionInterceptor
        ): ApiUtil {

            val builder = OkHttpClient.Builder()
            builder.connectTimeout(5, TimeUnit.SECONDS)
            builder.writeTimeout(10, TimeUnit.SECONDS)

            val interceptor = HttpLoggingInterceptor()
            interceptor.level = HttpLoggingInterceptor.Level.BODY

            val client = OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .addInterceptor(networkConnectionInterceptor)
                .build()


            builder.cache(null)

            val gson = GsonBuilder()
                .setLenient()
                .create()

            val retrofit = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(client)
                .build()

            return retrofit.create(ApiUtil::class.java)
            
        }

this is how i am calling the api’s

fun onGetNationalities() {
        try {
            responseCallback?.onStarted()
            Coroutines.main {
                val nationalitiesResponse = repository.getNationalities()
                nationalitiesResponse?.let {
                    if (it.success) {
                        responseCallback?.onSuccess(it)
                    } else {
                        responseCallback?.onFailure(it.errorMessage)
                    }
                }
            }
        } catch (e: Exception) {
            responseCallback?.onAlert(
                e.cause?.localizedMessage
                    ?: MessageUtils.SOMETHING_WENT_WRONG
            )
        } catch (e: NoInternetException) {
            responseCallback?.onAlert(e.message!!)
        }catch (e: NoConnectivityException) {
            responseCallback?.onAlert(e.message!!)
        }
    }

NoInternetException and NoConnectivityException are two exception classes

class NoConnectivityException : IOException() {
    override val message: String
        get() =
            "No network available, please check your WiFi or Data connection"
}
class NoInternetException() : IOException() {
    override val message: String
        get() =
            "No internet available, please check your connected WIFi or Data"
}

This is not happening everytime for example if i am calling an api on button click and internet is not active, exception thrown from the intercepor will be caught in the function from where i am calling the api but if i call an api on the start on e.g signup form so i can get the nationalities for signup as in my example the interceptor will crash while throwing the exception plus it also crash sometime on the chain.proceed with the Sockettimeoutexception etc. The crash is reproducible.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 23 (5 by maintainers)

Most upvoted comments

If you throw an unchecked exception in an interceptor, OkHttp will report that to the uncaught exception handler. You can throw IOException instead if you want to catch it.

Hi @swankjesse I am working on android native kotlin project. Please find below details from build.gradle file for latest okhttp and retrofit dependencies which I am using for project. okhttp implementation "com.squareup.okhttp3:okhttp:4.9.0" implementation "com.squareup.okhttp3:logging-interceptor:4.9.0" Retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

I have already set relevant timeouts for okhttp client.

.connectTimeout(HTTP_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(HTTP_READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(HTTP_WRITE_TIMEOUT, TimeUnit.SECONDS)

I am using okhttp3.Interceptor. Please find implementation below.

import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response

class AuthorizationInterceptor(private val sharedPreferenceService: SharedPreferenceService) :
    Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val requestBuilder = chain.request().newBuilder()
            .addHeader("Content-Type", "application/json")
            .addHeader("Accept", "application/json")

        return chain.proceed(requestBuilder.build())
    }
  }

Observations / Issues

  1. If API call is in progress and we try to change or disable network/internet, okhttp3 itself internally crashing as soon as below line executes from above snippets so App is crashing. return chain.proceed(requestBuilder.build())

I have figured out below okhttp internal crashes logs from my android studio that can help you to find root cause and fixes.

FATAL EXCEPTION: main
    Process: com.pg.posm, PID: 8770
    javax.net.ssl.SSLException: Read error: ssl=0x7afca87588: I/O error during system call, Software caused connection abort
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.pg.posm, PID: 26238
    java.net.ConnectException: Failed to connect to hostname/ip:port

I also tried to wrap above Interceptor snippets using try-catch but still okhttp3 itself crashing so app is crashing. We don’t have any control to solve it. Please find below Interceptor snippets with wrapped try-catch implementation.

import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import timber.log.Timber
import java.io.IOException

class AuthorizationInterceptor(private val sharedPreferenceService: SharedPreferenceService) :
    Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val requestBuilder = chain.request().newBuilder()
            .addHeader("Content-Type", "application/json")
            .addHeader("Accept", "application/json")
     
        try {
            return chain.proceed(requestBuilder.build())
        }catch (e: Throwable) {
            Timber.d("Outer")
            if (e is IOException) {
                Timber.d("If IOException=>%s", e)
                throw e
            } else {
                Timber.d("Else IOException=>%s", e)
                throw IOException(e)
            }
        }
    }   
}

You can also try to make small android project with relevant setup and snippets and see. Please provide necessary fixes for this blocker issue as many people are facing in the world. Please let me know in case you need more details. I would more than happy to provide you.

I have tried with these too but still crashing

implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'