Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Migrate callback code to suspended function

I'm re-factoring my Android code from Java to Kotlin using coroutines but i did not find an easy way to rewrite callback-based code to suspended functions.

A basic example would be an alert popup that returns a result, in Javascript it would be something like this:

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

I would translate in Kotlin to something like:

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)
        }
        //...
    }
}

The resolvable is a custom function i developed for this purpuse, here's the source code:

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
}

I quickly developed the resolvable function (keep in mind it's a quick draft) using lambdas and semaphores but i don't know if there are any pre-existing function (i could not found any) or can be optimized or has any drawback/problems.

Thanks.

like image 368
Plokko Avatar asked Dec 18 '22 01:12

Plokko


1 Answers

It looks that you are trying to reinvent suspendCoroutine function. I'd suggest to replace your resolvable function with invocation of suspendCoroutine to get the kind of functionality you are looking for:

    //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)
    }

If you perform "extract function" refactoring around suspendCoroutine block and name the resulting suspending function prompt, then you can write your code in a style that is very similar to JS.

You can also consider using suspendCancellebleCoroutine instead of a plain suspendCoroutine. This way you can support cancellation of the launched coroutine and install a handler to close your dialog when it is cancelled.

like image 198
Roman Elizarov Avatar answered Dec 28 '22 05:12

Roman Elizarov