diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt index 16c82fabd..72e87ed5f 100644 --- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt +++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt @@ -145,7 +145,7 @@ class Coroutine( context: CoroutineContext, block: suspend CoroutineScope.() -> T ): Job { - return (scope + Dispatchers.Main).launch(start = startOption) { + return (scope.plus(Dispatchers.Main)).launch(start = startOption) { try { start?.let { dispatchVoidCallback(this, it) } ensureActive() diff --git a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt index 23a3ea14c..f1a4a300f 100644 --- a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt @@ -1,35 +1,64 @@ package io.legado.app.utils +import android.util.Log +import androidx.core.os.postDelayed import com.script.SimpleBindings import io.legado.app.constant.AppConst import io.legado.app.exception.RegexTimeoutException +import io.legado.app.help.CrashHandler +import io.legado.app.help.coroutine.Coroutine +import kotlinx.coroutines.suspendCancellableCoroutine +import splitties.init.appCtx +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +private val handler by lazy { buildMainHandler() } /** * 带有超时检测的正则替换 */ -fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): String { - val startTime = System.currentTimeMillis() - val charSequence = this +suspend fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): String { + val charSequence = this@replace val isJs = replacement.startsWith("@js:") val replacement1 = if (isJs) replacement.substring(4) else replacement - val pattern = regex.toPattern() - val matcher = pattern.matcher(charSequence) - val stringBuffer = StringBuffer() - while (matcher.find()) { - if (System.currentTimeMillis() - startTime > timeout) { - val timeoutMsg = "替换超时,将禁用替换规则" - throw RegexTimeoutException(timeoutMsg) + return suspendCancellableCoroutine { block -> + val coroutine = Coroutine.async { + try { + val pattern = regex.toPattern() + val matcher = pattern.matcher(charSequence) + val stringBuffer = StringBuffer() + while (matcher.find()) { + if (isJs) { + val bindings = SimpleBindings() + bindings["result"] = matcher.group() + val jsResult = + AppConst.SCRIPT_ENGINE.eval(replacement1, bindings).toString() + matcher.appendReplacement(stringBuffer, jsResult) + } else { + matcher.appendReplacement(stringBuffer, replacement1) + } + } + matcher.appendTail(stringBuffer) + Log.e("regex", "end") + block.resume(stringBuffer.toString()) + } catch (e: Exception) { + block.resumeWithException(e) + } } - if (isJs) { - val bindings = SimpleBindings() - bindings["result"] = matcher.group() - val jsResult = AppConst.SCRIPT_ENGINE.eval(replacement1, bindings).toString() - matcher.appendReplacement(stringBuffer, jsResult) - } else { - matcher.appendReplacement(stringBuffer, replacement1) + handler.postDelayed(timeout) { + if (coroutine.isActive) { + val timeoutMsg = "替换超时,3秒后还未结束将重启应用\n替换规则$regex\n替换内容:${this}" + val exception = RegexTimeoutException(timeoutMsg) + block.cancel(exception) + appCtx.longToastOnUi(timeoutMsg) + CrashHandler.saveCrashInfo2File(exception) + handler.postDelayed(3000) { + if (coroutine.isActive) { + appCtx.restart() + } + } + } } } - matcher.appendTail(stringBuffer) - return stringBuffer.toString() }