6

我正在尝试将方法从 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
            }
        }
    }

返回 outputFile的行编译出错,谁能帮忙,这是我第一次使用协程

编辑

这是使用挂起后的方法,但是现在我不知道如果发生任何问题我该如何返回值

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

        val outputFile = withContext(Dispatchers.IO) {

            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)
            }catch (th: Throwable) {
            }
        }
    }

编辑 2

你的意思是这样

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(""))
        }
4

3 回答 3

2

如果你期望这个功能

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为 a suspend fun。您不能直接从 Android 回调中调用它。所以,在呼叫现场,写

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

这里请注意,我们调用launchwiththis作为接收者。的接收者launch必须是 aCoroutineScope并且你应该在你的MainActivity或者你调用它的任何上下文中实现它。有关解释,请参阅结构化并发。

于 2018-12-10T17:34:33.260 回答
1

解决它的一种可能方法是使用GlobalScope.async构建器:

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
    }
}
于 2018-12-10T16:52:59.340 回答
1

您可以像这样指定 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)
        }
    }
}
于 2018-12-10T16:54:51.517 回答