|
|
|
@ -1,6 +1,8 @@ |
|
|
|
|
package io.legado.app.help.coroutine |
|
|
|
|
|
|
|
|
|
import android.util.Log |
|
|
|
|
import kotlinx.coroutines.* |
|
|
|
|
import kotlin.coroutines.CoroutineContext |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) { |
|
|
|
@ -17,14 +19,12 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) |
|
|
|
|
|
|
|
|
|
private val job: Job |
|
|
|
|
|
|
|
|
|
private var start: (suspend CoroutineScope.() -> Unit)? = null |
|
|
|
|
private var execute: (suspend CoroutineScope.(T?) -> Unit)? = null |
|
|
|
|
private var success: (suspend CoroutineScope.(T?) -> Unit)? = null |
|
|
|
|
private var error: (suspend CoroutineScope.(Throwable) -> Unit)? = null |
|
|
|
|
private var finally: (suspend CoroutineScope.() -> Unit)? = null |
|
|
|
|
private var start: VoidCallback? = null |
|
|
|
|
private var success: Callback<T?>? = null |
|
|
|
|
private var error: Callback<Throwable>? = null |
|
|
|
|
private var finally: VoidCallback? = null |
|
|
|
|
|
|
|
|
|
private var timeMillis: Long? = null |
|
|
|
|
|
|
|
|
|
private var errorReturn: Result<T>? = null |
|
|
|
|
|
|
|
|
|
val isCancelled: Boolean |
|
|
|
@ -37,9 +37,7 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) |
|
|
|
|
get() = job.isCompleted |
|
|
|
|
|
|
|
|
|
init { |
|
|
|
|
this.job = scope.plus(Dispatchers.Main).launch { |
|
|
|
|
executeInternal(this@launch, block) |
|
|
|
|
} |
|
|
|
|
this.job = executeInternal(scope, block) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun timeout(timeMillis: () -> Long): Coroutine<T> { |
|
|
|
@ -62,28 +60,35 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) |
|
|
|
|
return this@Coroutine |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun onStart(start: (suspend CoroutineScope.() -> Unit)): Coroutine<T> { |
|
|
|
|
this.start = start |
|
|
|
|
return this@Coroutine |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun onExecute(execute: suspend CoroutineScope.(T?) -> Unit): Coroutine<T> { |
|
|
|
|
this.execute = execute |
|
|
|
|
fun onStart( |
|
|
|
|
context: CoroutineContext? = null, |
|
|
|
|
block: (suspend CoroutineScope.() -> Unit) |
|
|
|
|
): Coroutine<T> { |
|
|
|
|
this.start = VoidCallback(context, block) |
|
|
|
|
return this@Coroutine |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun onSuccess(success: suspend CoroutineScope.(T?) -> Unit): Coroutine<T> { |
|
|
|
|
this.success = success |
|
|
|
|
fun onSuccess( |
|
|
|
|
context: CoroutineContext? = null, |
|
|
|
|
block: suspend CoroutineScope.(T?) -> Unit |
|
|
|
|
): Coroutine<T> { |
|
|
|
|
this.success = Callback(context, block) |
|
|
|
|
return this@Coroutine |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun onError(error: suspend CoroutineScope.(Throwable) -> Unit): Coroutine<T> { |
|
|
|
|
this.error = error |
|
|
|
|
fun onError( |
|
|
|
|
context: CoroutineContext? = null, |
|
|
|
|
block: suspend CoroutineScope.(Throwable) -> Unit |
|
|
|
|
): Coroutine<T> { |
|
|
|
|
this.error = Callback(context, block) |
|
|
|
|
return this@Coroutine |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun onFinally(finally: suspend CoroutineScope.() -> Unit): Coroutine<T> { |
|
|
|
|
this.finally = finally |
|
|
|
|
fun onFinally( |
|
|
|
|
context: CoroutineContext? = null, |
|
|
|
|
block: suspend CoroutineScope.() -> Unit |
|
|
|
|
): Coroutine<T> { |
|
|
|
|
this.finally = VoidCallback(context, block) |
|
|
|
|
return this@Coroutine |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -96,50 +101,72 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) |
|
|
|
|
return job.invokeOnCompletion(handler) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private suspend fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) { |
|
|
|
|
tryCatch( |
|
|
|
|
{ |
|
|
|
|
start?.invoke(scope) |
|
|
|
|
val result = executeBlockIO(block, timeMillis ?: 0L) |
|
|
|
|
success?.invoke(scope, result) |
|
|
|
|
}, |
|
|
|
|
{ e -> |
|
|
|
|
private fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T): Job { |
|
|
|
|
return scope.plus(Dispatchers.Main).launch { |
|
|
|
|
try { |
|
|
|
|
start?.let { dispatchVoidCallback(this, it) } |
|
|
|
|
val value = executeBlock(scope, timeMillis ?: 0L, block) |
|
|
|
|
success?.let { dispatchCallback(this, value, it) } |
|
|
|
|
} catch (e: Throwable) { |
|
|
|
|
val consume: Boolean = errorReturn?.value?.let { value -> |
|
|
|
|
success?.invoke(scope, value) |
|
|
|
|
success?.let { dispatchCallback(this, value, it) } |
|
|
|
|
true |
|
|
|
|
} ?: false |
|
|
|
|
|
|
|
|
|
if (!consume) { |
|
|
|
|
error?.invoke(scope, e) |
|
|
|
|
error?.let { dispatchCallback(this, e, it) } |
|
|
|
|
} |
|
|
|
|
} finally { |
|
|
|
|
finally?.let { dispatchVoidCallback(this, it) } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
finally?.invoke(scope) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private suspend fun executeBlockIO(block: suspend CoroutineScope.() -> T, timeMillis: Long): T? { |
|
|
|
|
val execution = withContext(Dispatchers.IO) { |
|
|
|
|
val result = block() |
|
|
|
|
execute?.invoke(this, result) |
|
|
|
|
result |
|
|
|
|
private suspend inline fun dispatchVoidCallback(scope: CoroutineScope, callback: VoidCallback) { |
|
|
|
|
if (null == callback.context) { |
|
|
|
|
callback.block.invoke(scope) |
|
|
|
|
} else { |
|
|
|
|
withContext(scope.coroutineContext.plus(callback.context)) { |
|
|
|
|
callback.block.invoke(this) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return if (timeMillis > 0L) withTimeout(timeMillis) { execution } else execution |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private suspend fun tryCatch( |
|
|
|
|
tryBlock: suspend () -> Unit, |
|
|
|
|
errorBlock: (suspend (Throwable) -> Unit)? = null, |
|
|
|
|
finallyBlock: (suspend () -> Unit)? = null |
|
|
|
|
private suspend inline fun <R> dispatchCallback( |
|
|
|
|
scope: CoroutineScope, |
|
|
|
|
value: R, |
|
|
|
|
callback: Callback<R> |
|
|
|
|
) { |
|
|
|
|
try { |
|
|
|
|
tryBlock() |
|
|
|
|
} catch (e: Throwable) { |
|
|
|
|
errorBlock?.invoke(e) |
|
|
|
|
} finally { |
|
|
|
|
finallyBlock?.invoke() |
|
|
|
|
if (null == callback.context) { |
|
|
|
|
callback.block.invoke(scope, value) |
|
|
|
|
} else { |
|
|
|
|
withContext(scope.coroutineContext.plus(callback.context)) { |
|
|
|
|
callback.block.invoke(this, value) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private suspend inline fun executeBlock( |
|
|
|
|
scope: CoroutineScope, |
|
|
|
|
timeMillis: Long, |
|
|
|
|
noinline block: suspend CoroutineScope.() -> T |
|
|
|
|
): T? { |
|
|
|
|
return withContext(scope.coroutineContext.plus(Dispatchers.IO)) { |
|
|
|
|
if (timeMillis > 0L) withTimeout(timeMillis) { |
|
|
|
|
block() |
|
|
|
|
} else block() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private data class Result<out T>(val value: T?) |
|
|
|
|
|
|
|
|
|
private inner class VoidCallback( |
|
|
|
|
val context: CoroutineContext?, |
|
|
|
|
val block: suspend CoroutineScope.() -> Unit |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
private inner class Callback<VALUE>( |
|
|
|
|
val context: CoroutineContext?, |
|
|
|
|
val block: suspend CoroutineScope.(VALUE) -> Unit |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|