我正在尝试使用客户端gRPC流上传一个大文件(165 MB)。 该文件分为2 MB的块。
val channel = AndroidChannelBuilder
.forAddress("server address", 443)
.context(this)
.intercept(GrpcAuthorizationInterceptor())
.build()
val source = FileInputStream(File("/sdcard/Movies/Movie.mp4"))
FileServiceGrpc.newStub(channel)
.uploadFile(object : ClientResponseObserver<UploadFileRequest, UploadFileResponse> {
override fun beforeStart(requestStream: ClientCallStreamObserver<UploadFileRequest>) {
var counter = 0
requestStream.setOnReadyHandler {
while (requestStream.isReady) {
Log.d("MainActivity", "sending ${counter++} chunk")
val bytes = ByteArray(2 * 1024 * 1024)
source.read(bytes)
val byteString = ByteString.copyFrom(bytes)
val request = UploadFileRequest.newBuilder()
.setData(byteString)
.setExtension(UploadFileRequest.Extension.MP4)
.build()
requestStream.onNext(request)
}
}
}
override fun onNext(value: UploadFileResponse) {
}
override fun onError(t: Throwable) = throw t
override fun onCompleted() {
}
})
每次运行此代码时,应用程序都会崩溃:
java.lang.OutOfMemoryError: Failed to allocate a 24 byte allocation with 125656 free bytes and 122KB until OOM, target footprint 201326592, growth limit 201326592; failed due to fragmentation (largest possible contiguous allocation 0 bytes)
崩溃前的日志输出如下:
sending 0 chunk
...
sending 61 chunk
重要的是,每个sending x chunk
日志条目之间没有延迟,这意味着setOnReadyHandler
被立即调用,并且requestStream.isReady
标志保持true
。
似乎是这里的问题:即使先前的数据尚未发送并由gRPC缓冲,isReady
也会返回true
。 isReady
方法的JavaDoc表示以下内容:
* If {@code true}, indicates that the observer is capable of sending additional messages
* without requiring excessive buffering internally. This value is just a suggestion and the
* application is free to ignore it, however doing so may result in excessive buffering within the
* observer.
在android:largeHeap="true"
中设置AndroidManifests.xml
标志可以解决此问题,但是对我来说,这似乎只是一种解决方法。
如何改进代码以摆脱OutOfMemoryError
?