将回调代码迁移至挂起函数

6

我正在使用协程将我的Android代码从Java重构为Kotlin,但我没有找到一种简单的方法将基于回调的代码重写为挂起函数。

一个基本的例子是返回结果的警报弹出窗口,在Javascript中它可能是这样的:

let value = prompt("please insert a value")
console.log("Value:"+value)

我会将其翻译成 Kotlin,大致意思如下:
class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        //Standard activity initialization
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //Actual code...
        launch {
            val value = resolvable<String>(UI) { success, error ->
                //Build a simple popup prompt with AlertDialog
                val input = EditText(this@MainActivity)

                val builder = AlertDialog.Builder(this@MainActivity)
                        .setTitle("please insert a value")
                        .setView(input)
                        .setPositiveButton("Ok",{ dialog, id ->
                            success(input.text.toString())//This lambda returns the value
                        })
                val dialog = builder.create()
                dialog.show()
            }
            println("Value:"+ value)
        }
        //...
    }
}

Resolvable是我为此目的开发的自定义函数,以下是源代码:

import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.cancelAndJoin
import kotlinx.coroutines.experimental.launch
import java.util.concurrent.Semaphore
import kotlin.coroutines.experimental.CoroutineContext

suspend fun <T> resolvable(
        context: CoroutineContext = DefaultDispatcher,
        block: suspend (success:(T?)->Unit,error:(Throwable)->Unit) -> Unit
):T?{
    var result:T? = null
    var exception:Throwable? = null
    val semaphore = Semaphore(0)


    val job = launch(context){
        block({r:T? -> {
            result=r
            semaphore.release()
        }},{e:Throwable -> {
            exception=e
            semaphore.release()
        }})
    }

    semaphore.acquire()
    job.cancelAndJoin()

    if(exception!=null)
        throw exception!!
    return result
}

我使用lambda和semaphore快速开发了一个可解决函数(请记住这是一份快速草稿),但我不知道是否存在任何现有的函数(我找不到)。此函数是否可以优化或存在任何缺点/问题也不确定。

谢谢。


https://dev59.com/PqPia4cB1Zd3GeqPrwFj - Sach
@Sach,那不是我想要的!在这个例子中,代码只是从Java转换到Kotlin;我想避免使用回调模式,而是使用挂起函数。 - Plokko
1个回答

12

看起来你想要重新发明 suspendCoroutine 函数。我建议使用 suspendCoroutine 的调用来替换你的 resolvable 函数以获得所需的功能:

    //Actual code...
    launch(UI) {
        val value = suspendCoroutine<String> { cont ->
            //Build a simple popup prompt with AlertDialog
            val input = EditText(this@MainActivity)

            val builder = AlertDialog.Builder(this@MainActivity)
                    .setTitle("please insert a value")
                    .setView(input)
                    .setPositiveButton("Ok",{ dialog, id ->
                        cont.resume(input.text.toString()) //!!! Resume here
                    })
            val dialog = builder.create()
            dialog.show()
        }
        println("Value:"+ value)
    }
如果你在 suspendCoroutine 块周围执行“提取函数”重构,并将结果挂起的函数命名为 prompt,那么你可以使用与 JS 非常相似的样式编写代码。 你还可以考虑使用 suspendCancellebleCoroutine 替代普通的 suspendCoroutine。这样,你就可以支持已启动协程的取消,并安装一个处理程序以在其被取消时关闭对话框。

谢谢!我知道它存在,但是在任何文档中都找不到它!有没有办法像我的示例一样指定UI线程,还是我应该只是执行launch(UI) { suspendCoroutin<String>...... }? - Plokko
1
你应该在启动协程时指定线程。我已经在我的答案中更正了代码。 - Roman Elizarov
请@Roman,您能否提供一个suspendCancellebleCoroutine的示例?如果被取消,请关闭对话框或仅添加println。 - Plokko
这种模式有官方文档吗?看起来很有趣,但是找不到官方的文档。 - kravemir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接