Retrofit 2 流式传输大型 JSON 响应

Mar*_*ers 7 json kotlin retrofit okhttp

我正在尝试在接收(流式传输/分块)时解析/处理 JSON 响应。但我无法让它发挥作用。当我收到 JSON 时,我只能在收到整个响应后才能处理它。

如果我使用静态文件下载测试相同的代码,则它可以正常工作。

改造:2.6.1 OkHTTP 3.12.0

当我下载 5MB 文件时,将立即显示“调用确定”通知,稍后显示“下载完成”。对于 JSON 文件,“调用确定”将需要一段时间。

Logger.info("Start download")
val response = fileApi.download5MBFile() // Streaming
val response = dataHubApi.download3MBJson() //Does not stream
val response = articleApi.download10MBJson() // Does not stream
if (response.isSuccessful) {
    Logger.info("Call ok")
    val input = response.body()?.byteStream()
    val buffer = ByteArray(8192)
    var size = 0

    while (true) {
        val read = input!!.read(buffer)

        if (read == -1) {
            break
        }
        size += read
        //Logger.info("Progress: ${size/1024/1024}mb")
    }
    Logger.info("Download complete")
} else {
    Logger.info("Call not ok")
}
Run Code Online (Sandbox Code Playgroud)

创建 API 的工厂方法都是相同的,如下所示:


    fun create(): FileApi {
        val logLevel = Level.HEADERS

        val retrofit = Retrofit.Builder()
            .baseUrl(URL)
            .client(getOkHttpClient(logLevel))
            .build()

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

    private fun getOkHttpClient(logLevel: Level): OkHttpClient {
        return OkHttpClient.Builder()
            .readTimeout(60L, TimeUnit.SECONDS) // default retrofit value is 10sec
            .build()
    }
Run Code Online (Sandbox Code Playgroud)

并且所有的接口也都是相同的格式:

interface FileApi {
    @GET("/5MB.zip")
    @Streaming
    suspend fun download5MB(): Response<ResponseBody>
}
Run Code Online (Sandbox Code Playgroud)

我还能做什么来流式传输 JSON?

hta*_*oya 0

如果它那么大,可能是因为它包含多个项目的列表,而不是一个或一小块元素。

[
  { item1 },
  { item2 },
  ...
]
Run Code Online (Sandbox Code Playgroud)

即使它是 firebase json 表示形式,您也会得到以下内容:

{
  "key1": {element },
  "key2": {element },
  "key3": { element },
  ...
}
Run Code Online (Sandbox Code Playgroud)

如果您在解析完整的 json 时遇到困难,那么您很可能在将其保存到 kotlin 列表中时也会遇到一些麻烦,因此最好的办法是将其直接保存到数据库中,您可以在其中管理单个对象或页面稍后的对象。

因此,技巧是将 JSON 作为流式文本下载,并在流式传输时对其进行解析,并确定何时分割每个元素,这样您只需在文本块上将元素作为 JSON 处理,而不是处理所有文本。

例如,对于一个堆栈,您可能会开始添加大括号,但是一旦达到 0 级堆栈,您肯定会拥有一个元素的流并解析其中的文本。