Okio 3.x 下使用扩展函数将Retrofit响应体流式写入文件的正确实现方案(含资源安全关闭)
Okio 3.x 下使用扩展函数将Retrofit响应体流式写入文件的正确实现方案(含资源安全关闭)
嘿,刚好我最近也完成了Okio 3.x的迁移,碰到过一模一样的问题!这就给你拆解错误原因,再给出符合Okio 3.x idiom的正确写法,还能保证资源安全关闭~
先说说你遇到的错误原因
- 静态方法变扩展函数:Okio 3.x把
sink(File)和buffer(Sink)这类核心方法从静态工具类调用改成了扩展函数,所以原来Okio.sink(destination)、Okio.buffer(sink)的写法就失效了,得换成对象调用的形式。 - 空安全与类型匹配:
response.body()?.source()返回的是BufferedSource?,虽然BufferedSource是Source的子类,但空值需要处理,直接传给writeAll会触发类型不匹配的报错。
最小可行实现(同步调用)
下面是完整的同步版本代码,用Kotlin的use函数自动管理资源,无需手动调用close(),绝对安全:
import okhttp3.ResponseBody import okio.buffer import okio.sink import java.io.File // 假设你的Retrofit Api接口定义如下 interface Api { @GET fun downloadFileFromUrl(@Url url: String): Call<ResponseBody> } fun downloadFile(api: Api, url: String, destination: File) { val response = api.downloadFileFromUrl(url).execute() // 先确保请求成功且响应体非空,抛出明确的错误信息 val responseBody = requireNotNull(response.body()) { "下载失败:响应体为空" } check(response.isSuccessful) { "下载失败:HTTP状态码 ${response.code}" } // 用use函数自动关闭资源,异常时也会正确释放 destination.sink().buffer().use { bufferedSink -> responseBody.source().use { bufferedSource -> bufferedSink.writeAll(bufferedSource) } } }
关键要点拆解
- 扩展函数的正确用法:
- 原来的
Okio.sink(destination)→ 现在直接用destination.sink()(File的扩展函数) - 原来的
Okio.buffer(sink)→ 现在用sink.buffer()(Sink的扩展函数),还可以链式写成destination.sink().buffer()
- 原来的
- 资源安全关闭:
Kotlin的use函数是处理Closeable资源的最佳实践,它会在代码块执行完毕(不管正常结束还是抛出异常)自动调用close(),彻底避免资源泄漏,比手动写try-finally简洁太多。 - 空安全与响应校验:
用requireNotNull确保响应体非空,用check验证HTTP状态码是2xx,让错误信息更清晰,排查问题更方便。
协程版本(推荐,适配Retrofit协程支持)
如果你的项目用了Kotlin协程,Retrofit也支持suspend函数,写法会更简洁:
import okhttp3.ResponseBody import okio.buffer import okio.sink import java.io.File interface Api { @GET suspend fun downloadFileFromUrl(@Url url: String): ResponseBody } suspend fun downloadFileCoroutine(api: Api, url: String, destination: File) { // 协程调用下,Retrofit会直接返回ResponseBody(请求失败会抛出异常) val responseBody = api.downloadFileFromUrl(url) // 同样用use函数管理资源 destination.sink().buffer().use { bufferedSink -> responseBody.source().use { bufferedSource -> bufferedSink.writeAll(bufferedSource) } } }
协程版本里,Retrofit会自动处理请求的异常(比如HTTP错误会抛出HttpException),不需要手动校验isSuccessful,代码更清爽。
内容来源于stack exchange
相关产品推荐
相关产品推荐

