【问题标题】:How can Retrofit handle invalid responses from interceptor?Retrofit 如何处理来自拦截器的无效响应?
【发布时间】:2021-05-28 08:49:50
【问题描述】:

我已经花了几个小时试图弄清楚这件事,但我仍然可以弄清楚。

我正在尝试使用 JSON 从网站检索数据。 如果网站是活动的并且一切正常,它可以工作,但如果网站返回的不是数据,比如 403 错误或任何其他错误,那么它就会崩溃。我尝试调试它,但我仍然不明白这里发生了什么。

这是我的代码:

我有一个带有拦截器的 NetworkModule,它应该检查响应是否有效,据我所知它有效,因为我的变量 isDataRetrievable 为 false(默认值):

val networkModule = module {

    single {

        val customGson =
            GsonBuilder().registerTypeAdapter(Lesson::class.java, LessonDeserializer())
                .create()

        Retrofit.Builder()
            .client(get())
            .addConverterFactory(
                GsonConverterFactory.create(customGson)
            )
            .baseUrl(BuildConfig.URL)
            .build()
    }

    factory {

        OkHttpClient.Builder()
            .addInterceptor(Interceptor { chain ->
                chain.withConnectTimeout(1,TimeUnit.SECONDS)
                val request: Request = chain.request()
                val response = chain.proceed(request)

                if (response.isSuccessful){
                    networkStatus.isDataRetrievable = true
                }

                response
            }).build()
    }

    factory {
        get<Retrofit>().create(LessonApi::class.java)
    }

}

接下来,我有我的 API 来获取数据:

interface LessonApi {
    @GET("/JSON/json_get_data.php")
    suspend fun getLessons(): Call<Lesson>
}

然后,由于某种原因,我有一个存储库(我不是唯一处理此代码的人,我没有做这部分):

class LessonRepository(private val service: LessonApi) {
    suspend fun getLessons() = service.getLessons()
}

然后,我有我的启动屏幕视图模型,如果可能的话,它应该检索数据:

          if (networkStatus.isNetworkConnected && networkStatus.isWebsiteReachable) {
                var tmp = repository.getLessons()
                tmp.enqueue(object : Callback<Lesson> {

                    override fun onFailure(call: Call<Lesson>, t: Throwable) {
                        Log.d("DataFailure",t.message.toString())
                        nextScreenLiveData.postValue(false)
                    }

                    override fun onResponse(call: Call<Lesson>, response: Response<Lesson>) {
                        Log.d("DataFailure","Test")
                    }
                })
            }else{
                nextScreenLiveData.postValue(false)
            }

问题是当程序到达repository.getLessons()这一行时,它会崩溃并报错:

retrofit2.HttpException: HTTP 403 
        at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:49)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        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:919)

因此永远不会调用 onFailure 或 onResponse。我试图运行调试器以介入,但当它失败时我无法弄清楚。 我以为是因为它试图反序列化无效数据,但我在我的反序列化器中到处都设置了断点,它从来没有命中断点。

我不是专业的android开发者,但是在这里我很困惑。

我想做的是,如果请求不成功,只需丢弃响应(不要反序列化它),并显示消息或退出。

请帮忙,这太令人沮丧了。我不确定如何拦截错误或如果拦截器收到不成功的请求该怎么办(现在我只是设置了一个变量,但它没有使用)。

谢谢。

编辑: 我想做的是从网络服务器检索数据。如果它不能(出于任何原因),我不希望 gson 解析数据(因为它可能是垃圾并且不会对应于我的反序列化器)。但是,我觉得这个 okhttp / retrofit 是一个管道,其中 okhttp 从网络服务器获取响应并将其传递给一个 gson 转换器。我想要做的是拦截这个响应,如果它不成功,不将它传递给 gson,设置一个变量,以便我的应用程序的其余部分知道该做什么。但问题是,就目前而言,它甚至在进入队列中的回调之前就崩溃了。拦截器工作得很好,但如果它不成功,我希望他放弃响应。有可能吗?

【问题讨论】:

    标签: android error-handling retrofit retrofit2


    【解决方案1】:

    我尝试了类似的方法,它可以处理错误代码 (>400),但我也想处理格式错误的 JSON 数据,所以我添加了 onResponse 和 onFailure 回调,但它从来没有奏效,因为当我收到一个格式错误的JSON,它也会触发一个异常,然后在它可以进入'enqueue'之前进入catch,所以我不确定它是用来做什么的。

    try {
        val lessons = repository.getLessons().enqueue(object : Callback<List<Lesson>> {
            override fun onResponse(call: Call<List<Lesson>>, response: Response<List<Lesson>>) {
                networkStatus.isDataRetrievable = response.isSuccessful
                Log.d("Retrofit", "Successful response")
                nextScreenLiveData.postValue(response.isSuccessful)
            }
        
            override fun onFailure(call: Call<List<Lesson>>, t: Throwable) {
                Log.d("Retrofit", "Failure response")
                nextScreenLiveData.postValue(false)
            }
        })
        nextScreenLiveData.postValue(true)
        } catch (e: Exception) {
        nextScreenLiveData.postValue(false)
        }
    

    不管怎样,只要这段代码最终适用于所有事情:

    try {
        val lessons = repository.getLessons().filter {
            it.lesson.contains("video")
        }.filter {
            DataUtils.isANumber(it.id)
        }
        lessonDao.insertLessons(lessons)
        networkStatus.isDataRetrievable = true
    } catch (e: Exception) {
        networkStatus.isDataRetrievable = false
    }
    

    但在我的 API 中,我不返回回调,我直接返回对象,如下所示:

    @GET("/JSON/json_get_dat.php")
    suspend fun getLessons(): List<Lesson>
    

    我不知道这是否是正确的方法,但它确实有效。我希望这可能对其他人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-20
      • 2018-01-05
      • 2016-03-03
      • 1970-01-01
      • 2018-06-14
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      相关资源
      最近更新 更多