在拦截器中抛出 IOException 导致 Retrofit 崩溃
Throwing IOException in Interceptor is causing the crash with Retrofit
我正在尝试处理由于服务器和客户端网络连接引起的异常。
我制作了一个拦截器 class 用于在全局范围内处理这些错误作为方形文档。
每次我尝试在没有网络连接的情况下执行网络调用时,都会出现此错误并导致我的应用程序崩溃。
已编辑:如果连接速度太慢而无法触发 SocketTimeOutExeception ,它也会导致应用程序崩溃。我试图用一种只有在有活动的网络连接时才会执行网络调用的方式来覆盖它。但是就像我之前说的,连接速度太慢,错误与 SocketTimeOutExeception 一样。该应用程序在良好、活跃的网络连接下运行良好
W/System.err: java.net.UnknownHostException: Unable to resolve host "0725394a.ngrok.io": No address associated with hostname
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:157)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105)
at java.net.InetAddress.getAllByName(InetAddress.java:1154)
W/System.err: at okhttp3.Dns$Companion$DnsSystem.lookup(Dns.kt:49)
at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.kt:164)
at okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.kt:129)
at okhttp3.internal.connection.RouteSelector.next(RouteSelector.kt:71)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:197)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:107)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:75)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:245)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:74)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at com.network.di.AuthenticationInterceptor.intercept(AuthenticationInterceptor.kt:63)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at com.network.exception.NetworkExceptionInterceptor.intercept(NetworkExceptionInterceptor.kt:11)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at com.readystatesoftware.chuck.ChuckInterceptor.intercept(ChuckInterceptor.java:172)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:219)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:197)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:148)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:97)
W/System.err: at com.network.datasource.CourseDetailsDataSourcecImpl.getChaptersByPagination(CourseDetailsDataSourcecImpl.kt:39)
at com.data.repository.CourseDetailsRepositoryImpl.getChapterList(CourseDetailsRepositoryImpl.kt:19)
at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable.call(GetCourseChaptersByPagination.kt:26)
at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable.call(GetCourseChaptersByPagination.kt:11)
at io.reactivex.internal.operators.observable.ObservableFromCallable.subscribeActual(ObservableFromCallable.java:43)
at io.reactivex.Observable.subscribe(Observable.java:12246)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
这是我遇到的错误。
这是我的拦截器 classes
class AuthenticationInterceptor() : Interceptor {
private var bearerToken : String? = ""
private var token : String? = ""
private var customToken : String? = null
private var customTag : String? = null
private var onlyUseCustomHeader : Boolean = false
private var customName : String? = null
fun setCustom(customName : String? , customTag : String? , customToken : String?) {
this.customToken = customToken
this.customTag = customTag
this.customName = customName
}
fun setOnlyUseCustomHeader(onlyUseCustomHeader : Boolean) {
this.onlyUseCustomHeader = onlyUseCustomHeader
}
constructor(
bearerToken : String?,
token : String?
) : this() {
this.bearerToken = bearerToken
this.token = token
}
@Throws(IOException::class)
override fun intercept(chain : Interceptor.Chain) : Response {
val builder = chain.request().newBuilder()
if (!onlyUseCustomHeader) {
if (customName != null && customToken != null) {
if (customTag == null)
builder.addHeader("$customName" , "$customToken")
else
builder.addHeader("$customName" , "$customTag $customToken")
}
if (bearerToken != null && bearerToken != "")
builder.addHeader("Authorization" , "Bearer $bearerToken")
if (token != null && token != "")
builder.addHeader("token" , "$token")
} else {
if (customName != null && customToken != null) {
if (customTag == null)
builder.addHeader("$customName" , "$customToken")
else
builder.addHeader("$customName" , "$customTag $customToken")
}
}
builder.addHeader("Accept" , "application/json")
val request = builder.build()
return chain.proceed(request)
}
}
class NetworkExceptionInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
when (response.isSuccessful) {
true -> return response
false -> {
throw NetworkException(response.body , response.code)
}
}
}
}
这是我的自定义错误 class。
data class NetworkException constructor(
var errorBody: ResponseBody? = null,
var errorCode: Int
) : IOException()
这些是提供 OkhttpClient 和 Retrofit.Builder() 的方法,我在其中添加了网络拦截器和身份验证拦截器。
@Provides
fun providesOkHttpClientBuilder(context : Context) : OkHttpClient.Builder {
return OkHttpClient.Builder().apply {
val loggerInterceptor = HttpLoggingInterceptor().apply {
level = when (BuildConfig.DEBUG) {
true -> HttpLoggingInterceptor.Level.HEADERS
false -> HttpLoggingInterceptor.Level.NONE
}
}
addInterceptor(loggerInterceptor)
.addInterceptor(ChuckInterceptor(context))
.addInterceptor(NetworkExceptionInterceptor())
.readTimeout(8 , TimeUnit.SECONDS)
.writeTimeout(8 , TimeUnit.SECONDS)
.connectTimeout(8 , TimeUnit.SECONDS)
.cache(null)
}
}
@JvmStatic
@NonNull
@Named("authenticatedBuilder")
@Provides
fun getAuthenticatedBuilder(@Named("bearer_token") bearerToken : String? , @Named("token") token : String? , httpClientBuilder : OkHttpClient.Builder ,@Named("primary") retrofitBuilder : Retrofit.Builder) : Retrofit.Builder {
showLogD("NETWORK_MODULE" , "BearerToken before interception : $bearerToken")
showLogD("NETWORK_MODULE" , "TOKEN before interception : $token")
val interceptor : Interceptor =
AuthenticationInterceptor(bearerToken , token)
if (!httpClientBuilder.interceptors().contains(interceptor)) {
httpClientBuilder.addInterceptor(interceptor)
}
return retrofitBuilder.client(httpClientBuilder.build())
}
已编辑 2:这是 CourseDetailsDataSourceImpl.kt
class CourseDetailsDataSourcecImpl @Inject constructor(
private val courseDetailsService : CourseDetailsService ,
private val courseDetailsMapper : CourseDetailsMapper ,
private val chapterListMapper : CourseChapterListMapper ,
private val lessonSnapShotMapper : LessonSnapShotMapper ,
private val vdoPlayerOTPMapper : VdoPlayerOTPMapper
) : CourseDetailsDataSource {
@Inject
lateinit var vdoPlayerService : VdoPlayerService
override fun getCourseDetails(courseID : String) : CourseDetailsVO {
return courseDetailsMapper.map(courseDetailsService.getCourseDetails(courseID).execute().body()!!)
}
override fun getCourseVideoDetails(lessonID : String) : LessonSnapShotVO {
return lessonSnapShotMapper.map(courseDetailsService.getLessonDetails(lessonID).execute().body()!!)
}
override fun getChaptersByPagination(courseID : String , pageNumber : Int) : CourseChapterListVO{
//todo implement this with pagination
return chapterListMapper.map(courseDetailsService.getCourseChapters(courseID).execute().body()!!)
}
override fun getVdoPlayerOtp(videoID : String) : VdoPlayerOTPResponseVO {
return vdoPlayerOTPMapper.map(vdoPlayerService.getOtp( videoID , VDOOTPRequestBody()).execute().body()!!)
}
}
知道如何解决这个问题吗?
我正在使用 onErrorReturn 方法,因为我正在使用 flatmap 调用链中的多个网络调用。这样做的副作用是 onErrorReturn 不会消耗网络调用由于某种故障而抛出的异常,因此它会抛出异常,因此应用程序不可避免地会崩溃。这就是我改用 onErrorResumeNext 的原因。然后崩溃就消失了,因为它吞下了异常,抛出的异常可以自己处理。
我正在尝试处理由于服务器和客户端网络连接引起的异常。 我制作了一个拦截器 class 用于在全局范围内处理这些错误作为方形文档。 每次我尝试在没有网络连接的情况下执行网络调用时,都会出现此错误并导致我的应用程序崩溃。
已编辑:如果连接速度太慢而无法触发 SocketTimeOutExeception ,它也会导致应用程序崩溃。我试图用一种只有在有活动的网络连接时才会执行网络调用的方式来覆盖它。但是就像我之前说的,连接速度太慢,错误与 SocketTimeOutExeception 一样。该应用程序在良好、活跃的网络连接下运行良好
W/System.err: java.net.UnknownHostException: Unable to resolve host "0725394a.ngrok.io": No address associated with hostname
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:157)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105)
at java.net.InetAddress.getAllByName(InetAddress.java:1154)
W/System.err: at okhttp3.Dns$Companion$DnsSystem.lookup(Dns.kt:49)
at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.kt:164)
at okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.kt:129)
at okhttp3.internal.connection.RouteSelector.next(RouteSelector.kt:71)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:197)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:107)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:75)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:245)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:74)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at com.network.di.AuthenticationInterceptor.intercept(AuthenticationInterceptor.kt:63)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at com.network.exception.NetworkExceptionInterceptor.intercept(NetworkExceptionInterceptor.kt:11)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at com.readystatesoftware.chuck.ChuckInterceptor.intercept(ChuckInterceptor.java:172)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:219)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:197)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:148)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:97)
W/System.err: at com.network.datasource.CourseDetailsDataSourcecImpl.getChaptersByPagination(CourseDetailsDataSourcecImpl.kt:39)
at com.data.repository.CourseDetailsRepositoryImpl.getChapterList(CourseDetailsRepositoryImpl.kt:19)
at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable.call(GetCourseChaptersByPagination.kt:26)
at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable.call(GetCourseChaptersByPagination.kt:11)
at io.reactivex.internal.operators.observable.ObservableFromCallable.subscribeActual(ObservableFromCallable.java:43)
at io.reactivex.Observable.subscribe(Observable.java:12246)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
这是我遇到的错误。
这是我的拦截器 classes
class AuthenticationInterceptor() : Interceptor {
private var bearerToken : String? = ""
private var token : String? = ""
private var customToken : String? = null
private var customTag : String? = null
private var onlyUseCustomHeader : Boolean = false
private var customName : String? = null
fun setCustom(customName : String? , customTag : String? , customToken : String?) {
this.customToken = customToken
this.customTag = customTag
this.customName = customName
}
fun setOnlyUseCustomHeader(onlyUseCustomHeader : Boolean) {
this.onlyUseCustomHeader = onlyUseCustomHeader
}
constructor(
bearerToken : String?,
token : String?
) : this() {
this.bearerToken = bearerToken
this.token = token
}
@Throws(IOException::class)
override fun intercept(chain : Interceptor.Chain) : Response {
val builder = chain.request().newBuilder()
if (!onlyUseCustomHeader) {
if (customName != null && customToken != null) {
if (customTag == null)
builder.addHeader("$customName" , "$customToken")
else
builder.addHeader("$customName" , "$customTag $customToken")
}
if (bearerToken != null && bearerToken != "")
builder.addHeader("Authorization" , "Bearer $bearerToken")
if (token != null && token != "")
builder.addHeader("token" , "$token")
} else {
if (customName != null && customToken != null) {
if (customTag == null)
builder.addHeader("$customName" , "$customToken")
else
builder.addHeader("$customName" , "$customTag $customToken")
}
}
builder.addHeader("Accept" , "application/json")
val request = builder.build()
return chain.proceed(request)
}
}
class NetworkExceptionInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
when (response.isSuccessful) {
true -> return response
false -> {
throw NetworkException(response.body , response.code)
}
}
}
}
这是我的自定义错误 class。
data class NetworkException constructor(
var errorBody: ResponseBody? = null,
var errorCode: Int
) : IOException()
这些是提供 OkhttpClient 和 Retrofit.Builder() 的方法,我在其中添加了网络拦截器和身份验证拦截器。
@Provides
fun providesOkHttpClientBuilder(context : Context) : OkHttpClient.Builder {
return OkHttpClient.Builder().apply {
val loggerInterceptor = HttpLoggingInterceptor().apply {
level = when (BuildConfig.DEBUG) {
true -> HttpLoggingInterceptor.Level.HEADERS
false -> HttpLoggingInterceptor.Level.NONE
}
}
addInterceptor(loggerInterceptor)
.addInterceptor(ChuckInterceptor(context))
.addInterceptor(NetworkExceptionInterceptor())
.readTimeout(8 , TimeUnit.SECONDS)
.writeTimeout(8 , TimeUnit.SECONDS)
.connectTimeout(8 , TimeUnit.SECONDS)
.cache(null)
}
}
@JvmStatic
@NonNull
@Named("authenticatedBuilder")
@Provides
fun getAuthenticatedBuilder(@Named("bearer_token") bearerToken : String? , @Named("token") token : String? , httpClientBuilder : OkHttpClient.Builder ,@Named("primary") retrofitBuilder : Retrofit.Builder) : Retrofit.Builder {
showLogD("NETWORK_MODULE" , "BearerToken before interception : $bearerToken")
showLogD("NETWORK_MODULE" , "TOKEN before interception : $token")
val interceptor : Interceptor =
AuthenticationInterceptor(bearerToken , token)
if (!httpClientBuilder.interceptors().contains(interceptor)) {
httpClientBuilder.addInterceptor(interceptor)
}
return retrofitBuilder.client(httpClientBuilder.build())
}
已编辑 2:这是 CourseDetailsDataSourceImpl.kt
class CourseDetailsDataSourcecImpl @Inject constructor(
private val courseDetailsService : CourseDetailsService ,
private val courseDetailsMapper : CourseDetailsMapper ,
private val chapterListMapper : CourseChapterListMapper ,
private val lessonSnapShotMapper : LessonSnapShotMapper ,
private val vdoPlayerOTPMapper : VdoPlayerOTPMapper
) : CourseDetailsDataSource {
@Inject
lateinit var vdoPlayerService : VdoPlayerService
override fun getCourseDetails(courseID : String) : CourseDetailsVO {
return courseDetailsMapper.map(courseDetailsService.getCourseDetails(courseID).execute().body()!!)
}
override fun getCourseVideoDetails(lessonID : String) : LessonSnapShotVO {
return lessonSnapShotMapper.map(courseDetailsService.getLessonDetails(lessonID).execute().body()!!)
}
override fun getChaptersByPagination(courseID : String , pageNumber : Int) : CourseChapterListVO{
//todo implement this with pagination
return chapterListMapper.map(courseDetailsService.getCourseChapters(courseID).execute().body()!!)
}
override fun getVdoPlayerOtp(videoID : String) : VdoPlayerOTPResponseVO {
return vdoPlayerOTPMapper.map(vdoPlayerService.getOtp( videoID , VDOOTPRequestBody()).execute().body()!!)
}
}
知道如何解决这个问题吗?
我正在使用 onErrorReturn 方法,因为我正在使用 flatmap 调用链中的多个网络调用。这样做的副作用是 onErrorReturn 不会消耗网络调用由于某种故障而抛出的异常,因此它会抛出异常,因此应用程序不可避免地会崩溃。这就是我改用 onErrorResumeNext 的原因。然后崩溃就消失了,因为它吞下了异常,抛出的异常可以自己处理。