Skip to content

Commit

Permalink
Implement async/await for Android using Kotlin Coroutines
Browse files Browse the repository at this point in the history
  • Loading branch information
pilgr committed Aug 19, 2016
1 parent 4389ebb commit 834cdd0
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 1 deletion.
16 changes: 16 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.jakewharton.hugo'

buildscript {
repositories {
jcenter()
maven {
url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
}
}

android {
compileSdkVersion 24
Expand Down
40 changes: 40 additions & 0 deletions app/src/main/kotlin/coroutineandroid/AsyncUi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package coroutineandroid

import android.os.Handler
import android.os.Looper
import kotlin.coroutines.Continuation

fun asyncUi(coroutine c: AsyncController.() -> Continuation<Unit>): AsyncController {
val controller = AsyncController()
// TODO If not in UI thread - force run resume() in UI thread
controller.c().resume(Unit)
return controller
}

class AsyncController {
private var errorHandler: ErrorHandler? = null
private val uiHandler = Handler(Looper.getMainLooper())

suspend fun <V> await(f: () -> V, machine: Continuation<V>) {
Thread {
try {
val value = f()
uiHandler.post {
machine.resume(value)
}
} catch (e: Exception) {
uiHandler.post {
errorHandler?.invoke(e) ?: machine.resumeWithException(e)
}
}

}.start()

}

fun onError(errorHandler: ErrorHandler) {
this.errorHandler = errorHandler
}
}

typealias ErrorHandler = (Exception) -> Unit
60 changes: 60 additions & 0 deletions app/src/main/kotlin/coroutineandroid/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,72 @@ package coroutineandroid

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import com.example.coroutineandroid.R
import hugo.weaving.DebugLog
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
//startCoroutine()
startCoroutineUsingMoreConvenientErrorHandling()
}
}

@DebugLog
private fun startCoroutine() {
asyncUi {
progress.visibility = View.VISIBLE
text.text = "Loading..."
try {
// Release main thread and wait until text loaded
// Progress animation shown during loading
val loadedText = await(::loadText)
// Loaded successfully, come back in UI thread and show result
text.text = loadedText + " (to be processed)"
// Oh ah we need to run more processing in background
text.text = await { processText(loadedText) }
} catch (e: Exception) {
// Exception could be thrown in UI or background thread
// but handled in UI thread
text.text = e.message
}
progress.visibility = View.INVISIBLE
}
}

@DebugLog
private fun startCoroutineUsingMoreConvenientErrorHandling() {
asyncUi {
progress.visibility = View.VISIBLE
text.text = "Loading..."
// Release main thread and wait until text loaded
// Progress animation shown during loading
val loadedText = await(::loadText)
// Loaded successfully, come back in UI thread and show result
text.text = loadedText + " (to be processed)"
// Oh ah we need to run more processing in background
text.text = await { processText(loadedText) }
progress.visibility = View.INVISIBLE
}.onError {
text.text = it.message
progress.visibility = View.INVISIBLE
}
}

}
@DebugLog
private fun loadText(): String {
Thread.sleep(1000)
//if (1 == 1) throw RuntimeException("You are in the wrong place")
return "Loaded Text"
}
@DebugLog
private fun processText(input: String): String {
Thread.sleep(2000)
return "Processed $input"
}
24 changes: 23 additions & 1 deletion app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,30 @@
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="coroutineandroid.MainActivity">

<ProgressBar
android:id="@+id/progress"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:indeterminate="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="invisible"/>

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:textSize="18sp"
tools:text="Bla Bla"/>

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
android:layout_below="@id/text"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:text="Start"/>
</RelativeLayout>

0 comments on commit 834cdd0

Please sign in to comment.