Kotlin сопрограмма для выполнения внешнего процесса - PullRequest
2 голосов
/ 15 марта 2020

Традиционный подход в Java для выполнения внешнего процесса состоит в том, чтобы запустить новый Process, запустить два потока для использования его inputStream и errorStream, а затем вызвать его блокировку Process.waitFor(), чтобы дождаться внешнего команда завершена.

Как это можно сделать в (почти) неблокирующем стиле с Kotlin сопрограммами?

Я пробовал таким образом. Есть ли у вас какие-либо предложения по его улучшению?

(Как асинхронно читать потоки, также вызвать ProcessBuilder.start() в withContext(Dispatchers.IO), слишком много обращений к Dispatchers.IO, ...?)

    suspend fun executeCommand(commandArgs: List<String>): ExecuteCommandResult {
        try {
            val process = ProcessBuilder(commandArgs).start()

            val outputStream = GlobalScope.async(Dispatchers.IO) { readStream(process.inputStream) }
            val errorStream = GlobalScope.async(Dispatchers.IO) { readStream(process.errorStream) }

            val exitCode = withContext(Dispatchers.IO) {
                process.waitFor()
            }

            return ExecuteCommandResult(exitCode, outputStream.await(), errorStream.await())
        } catch (e: Exception) {
            return ExecuteCommandResult(-1, "", e.localizedMessage)
        }
    }

    private suspend fun readStream(inputStream: InputStream): String {
        val readLines = mutableListOf<String>()

        withContext(Dispatchers.IO) {
            try {
                inputStream.bufferedReader().use { reader ->
                    var line: String?

                    do {
                        line = reader.readLine()

                        if (line != null) {
                            readLines.add(line)
                        }
                    } while (line != null)
                }
            } catch (e: Exception) {
                // ..
            }
        }

        return readLines.joinToString(System.lineSeparator())
    }
...