2021-05-22 00:03:16 +02:00
|
|
|
package jp.juggler.util
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2022-05-29 15:38:21 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-05-27 04:15:59 +02:00
|
|
|
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
|
|
|
|
import kotlinx.coroutines.*
|
|
|
|
import java.lang.ref.WeakReference
|
2021-05-22 00:03:16 +02:00
|
|
|
import kotlin.coroutines.CoroutineContext
|
|
|
|
import kotlin.coroutines.EmptyCoroutineContext
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
private val log = LogCategory("EndlessScope")
|
|
|
|
|
2021-06-20 15:12:25 +02:00
|
|
|
val <T : Any> T.wrapWeakReference: WeakReference<T>
|
2021-05-27 04:15:59 +02:00
|
|
|
get() = WeakReference(this)
|
|
|
|
|
2021-05-22 00:03:16 +02:00
|
|
|
// kotlinx.coroutines 1.5.0 で GlobalScopeがdeprecated になったが、
|
|
|
|
// プロセスが生きてる間ずっと動いててほしいものや特にキャンセルのタイミングがないコルーチンでは使い続けたい
|
2022-06-13 19:23:46 +02:00
|
|
|
object EndlessScope : CoroutineScope {
|
2021-05-22 00:03:16 +02:00
|
|
|
override val coroutineContext: CoroutineContext
|
|
|
|
get() = EmptyCoroutineContext
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
|
|
|
// メインスレッド上で動作するコルーチンを起動して、終了を待たずにリターンする。
|
|
|
|
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
|
|
|
fun launchMain(block: suspend CoroutineScope.() -> Unit): Job =
|
2022-01-05 05:22:44 +01:00
|
|
|
EndlessScope.launch(context = Dispatchers.Main.immediate) {
|
2021-05-27 04:15:59 +02:00
|
|
|
try {
|
|
|
|
block()
|
|
|
|
} catch (ex: CancellationException) {
|
|
|
|
log.trace(ex, "launchMain: cancelled.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default Dispatcherで動作するコルーチンを起動して、終了を待たずにリターンする。
|
|
|
|
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
|
|
|
fun launchDefault(block: suspend CoroutineScope.() -> Unit): Job =
|
|
|
|
EndlessScope.launch(context = Dispatchers.Default) {
|
|
|
|
try {
|
|
|
|
block()
|
|
|
|
} catch (ex: CancellationException) {
|
|
|
|
log.trace(ex, "launchDefault: cancelled.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IOスレッド上で動作するコルーチンを起動して、終了を待たずにリターンする。
|
|
|
|
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
|
|
|
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
|
|
|
|
EndlessScope.launch(context = Dispatchers.IO) {
|
|
|
|
try {
|
|
|
|
block()
|
|
|
|
} catch (ex: CancellationException) {
|
|
|
|
log.trace(ex, "launchIO: cancelled.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IOスレッド上で動作するコルーチンを起動して、終了を待たずにリターンする。
|
|
|
|
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
|
|
|
// asyncの場合キャンセル例外のキャッチは呼び出し側で行う必要がある
|
|
|
|
@Suppress("DeferredIsResult")
|
|
|
|
fun <T : Any?> asyncIO(block: suspend CoroutineScope.() -> T): Deferred<T> =
|
|
|
|
EndlessScope.async(block = block, context = Dispatchers.IO)
|
|
|
|
|
2022-05-30 14:32:12 +02:00
|
|
|
fun AppCompatActivity.launchAndShowError(
|
|
|
|
errorCaption: String? = null,
|
|
|
|
block: suspend CoroutineScope.() -> Unit,
|
|
|
|
): Job = lifecycleScope.launch() {
|
|
|
|
try {
|
|
|
|
block()
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
showError(ex, errorCaption)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
suspend fun <T : Any?> AppCompatActivity.runWithProgress(
|
|
|
|
caption: String,
|
|
|
|
doInBackground: suspend CoroutineScope.(ProgressDialogEx) -> T,
|
|
|
|
afterProc: suspend CoroutineScope.(result: T) -> Unit = {},
|
|
|
|
progressInitializer: suspend CoroutineScope.(ProgressDialogEx) -> Unit = {},
|
|
|
|
preProc: suspend CoroutineScope.() -> Unit = {},
|
2022-05-30 14:32:12 +02:00
|
|
|
postProc: suspend CoroutineScope.() -> Unit = {},
|
2021-05-27 04:15:59 +02:00
|
|
|
) {
|
|
|
|
coroutineScope {
|
|
|
|
if (!isMainThread) error("runWithProgress: not main thread.")
|
|
|
|
|
|
|
|
val progress = ProgressDialogEx(this@runWithProgress)
|
|
|
|
|
|
|
|
val task = async(Dispatchers.IO) {
|
|
|
|
doInBackground(progress)
|
|
|
|
}
|
|
|
|
|
|
|
|
launch(Dispatchers.Main) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
preProc()
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
progress.setCancelable(true)
|
|
|
|
progress.setOnCancelListener { task.cancel() }
|
|
|
|
progress.isIndeterminateEx = true
|
2021-06-20 15:12:25 +02:00
|
|
|
progress.setMessageEx("$caption…")
|
2021-05-27 04:15:59 +02:00
|
|
|
progressInitializer(progress)
|
|
|
|
progress.show()
|
|
|
|
|
|
|
|
try {
|
|
|
|
val result = try {
|
|
|
|
task.await()
|
2021-06-20 15:12:25 +02:00
|
|
|
} catch (ignored: CancellationException) {
|
2021-05-27 04:15:59 +02:00
|
|
|
null
|
|
|
|
}
|
|
|
|
if (result != null) afterProc(result)
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
showToast(ex, "$caption failed.")
|
|
|
|
} finally {
|
|
|
|
progress.dismissSafe()
|
|
|
|
try {
|
|
|
|
postProc()
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|