Last active
May 26, 2019 04:21
-
-
Save duangsuse/dfc532a16f547459bcf3f4f8d922d849 to your computer and use it in GitHub Desktop.
Essay: Android cancellable asynchronous task API abstractoin in Kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.duangsuse.essay.android.Async | |
import android.os.AsyncTask | |
/** | |
* Job to be executed in worker thread, returning result with type `R` | |
*/ | |
@FunctionalInterface | |
interface BackgroundJob<out R> { | |
/** | |
* Logic to launch in worker thread | |
*/ | |
@Throws(Throwable::class) | |
fun doInBackground(): R | |
} | |
/** | |
* `BackgroundJob` lifecycle events | |
*/ | |
sealed class JobLifecycleEvent<out T> { | |
/** | |
* Task lifecycle event handler | |
*/ | |
@FunctionalInterface | |
interface Handler<T> { | |
fun handleEvent(ev: JobLifecycleEvent<T>) | |
} | |
/** | |
* Task started event | |
*/ | |
object StartEvent : JobLifecycleEvent<Nothing>() | |
/** | |
* Task finished event | |
*/ | |
class ResultEvent<T>(val result: T) : JobLifecycleEvent<T>() | |
/** | |
* Task error event | |
*/ | |
class ErrorEvent(val error: Throwable) : JobLifecycleEvent<Nothing>() | |
} | |
/** | |
* Cancellable task | |
*/ | |
interface Cancellable { | |
/** | |
* Cancel this task, if not cancelled | |
*/ | |
fun cancel() | |
/** | |
* Is this task cancelled? | |
*/ | |
fun isCancelled(): Boolean | |
} | |
/** | |
* Group combination of cancellable task | |
*/ | |
class CancellableGroup(private val cs: Set<Cancellable> = mutableSetOf()): Set<Cancellable> by cs, Cancellable { | |
override fun isCancelled(): Boolean = cs.all(Cancellable::isCancelled) | |
override fun cancel() = cs.forEach(Cancellable::cancel) | |
} | |
/** | |
* Android `AsyncTask` Cancellable | |
*/ | |
class AsyncTaskCancellable<T, R>(private val task: AsyncTask<in T, *, out R>): Cancellable { | |
override fun cancel() { if (!isCancelled()) task.cancel(true) } | |
override fun isCancelled(): Boolean = task.isCancelled | |
} | |
/** | |
* Android AsyncTask Abstraction | |
*/ | |
object Async { | |
/** | |
* Launch an asynchronous task, with given listener, returning cancellable token | |
*/ | |
fun <R> launchAsyncCancellable(job: BackgroundJob<R>, listener: JobLifecycleEvent.Handler<R>): Cancellable { | |
val task = object : AsyncTask<Nothing?, Nothing, R?>() { | |
override fun onPreExecute() = listener.handleEvent(JobLifecycleEvent.StartEvent) | |
override fun doInBackground(vararg params: Nothing?): R? { | |
try { return job.doInBackground() } | |
catch (ex: Throwable) { listener.handleEvent(JobLifecycleEvent.ErrorEvent(ex)) } | |
return null | |
} | |
override fun onPostExecute(result: R?) { result?.let { listener.handleEvent(JobLifecycleEvent.ResultEvent(it)) } } | |
} | |
return AsyncTaskCancellable(task).also { task.execute() } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment