OutOfMemoryError при потоковой передаче клиентом запроса gRP C - PullRequest
0 голосов
/ 18 июня 2020

Я пытаюсь загрузить большой файл (165 МБ), используя клиентскую потоковую передачу gRP C. Файл разбит на фрагменты по 2 МБ.

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)

Вывод журнала перед cra sh выглядит следующим образом :

sending 0 chunk
...
sending 61 chunk

Что важно, нет задержки между каждой sending x chunk записью журнала, что означает, что setOnReadyHandler вызывается немедленно, а флаг requestStream.isReady остается true.

Похоже, вот в чем проблема: хотя предыдущие данные не были отправлены и буферизированы gRP C, isReady возвращает true. Метод JavaDo c of isReady говорит следующее:

   * 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?

...