Котлин сопрограммы возвращают String - PullRequest
0 голосов
/ 10 декабря 2018

Я пытаюсь преобразовать метод из Java в kotlin и заменить AsyncTask на сопрограммы, но я не знаю, как вернуть значение из сопрограмм

Это мой метод

override fun getCompressedVideo(context:Context ,video: Uri) {

        GlobalScope.launch(Dispatchers.Main) {

            val inputFile = video.getRealPathFromVideoUri(context)
            val loadJNI: LoadJNI = LoadJNI();
            try {

                val workFolder: String = context.filesDir.absolutePath

                val outputFile: String = getFileFullName(
                    FilesConstants.VIDEO_FOLDER,
                    String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
                );

                val complexCommand = arrayOf (
                    "ffmpeg", "-y"
                    , "-i", inputFile
                    , "-strict", "experimental"
                    , "-s", "320x240"
                    , "-r", "25"
                    , "-aspect", "4:3"
                    , "-ab", "48000"
                    , "-ac", "2"
                    , "-vcodec", "mpeg4"
                    , "-movflags", "+faststart"
                    , "-ar", "22050"
                    , "-b", "2097k"
                    , outputFile);

                loadJNI.run(complexCommand, workFolder, context);
                return outputFile

            } catch (th: Throwable) {
                return@launch
            }
        }
    }

строка return outputFile делает ошибку компиляции, может кто-нибудь, пожалуйста, помогите, я впервые использую сопрограммы

EDIT

здесьэто метод после использования suspend, но теперь я не знаю, как я могу вернуть значение, если возникнут какие-либо проблемы1022 *

override suspend fun getCompressedVideo(context: Context, video: Uri) : String {

        try {
            val retValue = withContext(Dispatchers.IO) {

                val inputFile = video.getRealPathFromVideoUri(context)
                val loadJNI: LoadJNI = LoadJNI()

                val workFolder: String = context.filesDir.absolutePath

                val outputFile: String = getFileFullName(
                    FilesConstants.VIDEO_FOLDER,
                    String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
                )

                val complexCommand = arrayOf(
                    "ffmpeg", "-y"
                    , "-i", inputFile
                    , "-strict", "experimental"
                    , "-s", "320x240"
                    , "-r", "25"
                    , "-aspect", "4:3"
                    , "-ab", "48000"
                    , "-ac", "2"
                    , "-vcodec", "mpeg4"
                    , "-movflags", "+faststart"
                    , "-ar", "22050"
                    , "-b", "2097k"
                    , outputFile
                )

                loadJNI.run(complexCommand, workFolder, context)
            }

            return retValue.toString()
        } catch (th: Throwable) {
            return ""
        }
    }

и назовите это как

GlobalScope.launch {
            val retValue = ffmpegFacade.getCompressedVideo(this@TestActivity, Uri.parse(""))
        }

Ответы [ 3 ]

0 голосов
/ 10 декабря 2018

Вы можете указать функцию возврата Kotlin , например, так:

override fun getCompressedVideo(context: Context, video: Uri): String {

Однако вы все равно не можете этого сделать, потому что она асинхронная, функция синхронная.

Чтобы вернуться из метода, ему придется подождать, пока он не завершится, что противоречит всей цели асинхронного выполнения этого.

Вместо этого можно использовать функцию высокого порядка , чтобы указать, чтоделать с данными после завершения асинхронной задачи.

override fun getCompressedVideo(context:Context ,video: Uri, action: (String?) -> Unit) {

    GlobalScope.launch(Dispatchers.Main) {

        val inputFile = video.getRealPathFromVideoUri(context)
        val loadJNI: LoadJNI = LoadJNI();
        try {

            val workFolder: String = context.filesDir.absolutePath

            val outputFile: String = getFileFullName(
                FilesConstants.VIDEO_FOLDER,
                String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
            );

            val complexCommand = arrayOf (
                "ffmpeg", "-y"
                , "-i", inputFile
                , "-strict", "experimental"
                , "-s", "320x240"
                , "-r", "25"
                , "-aspect", "4:3"
                , "-ab", "48000"
                , "-ac", "2"
                , "-vcodec", "mpeg4"
                , "-movflags", "+faststart"
                , "-ar", "22050"
                , "-b", "2097k"
                , outputFile);

            loadJNI.run(complexCommand, workFolder, context);
            action(outputFile)

        } catch (th: Throwable) {
            action(null)
        }
    }
}
0 голосов
/ 10 декабря 2018

Если вы ожидаете, что эта функция

override fun getCompressedVideo(context: Context, video: Uri)

вернется, когда сжатие уже выполнено, это не так, как она может работать.Ваш код запускает параллельную задачу, которая будет выполнена в произвольное время после того, как ваш getCompressedVideo вернется.

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

override suspend fun getCompressedVideo(
        context: Context, video: Uri
): String? = withContext(Dispatchers.IO) {
    try {
        val inputFile = video.getRealPathFromVideoUri(context)
        val loadJNI = LoadJNI()
        val workFolder: String = context.filesDir.absolutePath
        val outputFile: String = getFileFullName(
                FilesConstants.VIDEO_FOLDER,
                String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT,
                        System.currentTimeMillis())
        )
        val complexCommand = arrayOf("-i", inputFile, "other-params")
        loadJNI.run(complexCommand, workFolder, context);
        outputFile
    } catch (t: Throwable) {
        null
    }
}

Как вы можетевидите, это означает изменение объявления getCompressedVideo на suspend fun.Вы не можете позвонить прямо из обратного вызова Android.Итак, на месте вызова напишите

this.launch {
    val videoFile = ffmpegfacade.getCompressedVideo(context, Uri.parse("example.org/video"))
    // continue processing on the UI thread using videoFile
}

Здесь обратите внимание, что мы вызываем launch с this в качестве получателя.Получатель launch должен быть CoroutineScope, и вы должны реализовать его в своем MainActivity или в любом другом контексте, из которого вы его вызываете.См. структурированный параллелизм для объяснения.

0 голосов
/ 10 декабря 2018

Один из возможных способов ее решения - использовать GlobalScope.async builder:

fun getCompressedVideo() = GlobalScope.async {
    val outputFile: String = "" 

    // ... compress video

    outputFile
}

// Calling getCompressedVideo() from outside
fun compressVideoAsync() {
    GlobalScope.launch(Dispatchers.Main) {
        val compression = getCompressedVideo()
        val outputFile = compression.await() // wait for result of compression operation without blocking the main thread

        // outputFile is ready to use
    }
}
...