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 b379c62f7..d3ac1778f 100644 --- a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt @@ -1,13 +1,16 @@ package io.legado.app.utils -import androidx.core.os.postDelayed import com.script.ScriptBindings import com.script.rhino.RhinoScriptEngine import io.legado.app.exception.RegexTimeoutException import io.legado.app.help.CrashHandler import io.legado.app.help.coroutine.Coroutine import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.selects.onTimeout +import kotlinx.coroutines.selects.select import kotlinx.coroutines.suspendCancellableCoroutine import splitties.init.appCtx import java.util.regex.Matcher @@ -19,47 +22,52 @@ private val handler by lazy { buildMainHandler() } /** * 带有超时检测的正则替换 */ +@OptIn(ExperimentalCoroutinesApi::class) 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 return runBlocking { suspendCancellableCoroutine { block -> - val coroutine = Coroutine.async(executeContext = IO) { - try { - val pattern = regex.toPattern() - val matcher = pattern.matcher(charSequence) - val stringBuffer = StringBuffer() - while (matcher.find()) { - if (isJs) { - val jsResult = RhinoScriptEngine.run { - val bindings = ScriptBindings() - bindings["result"] = matcher.group() - eval(replacement1, bindings) - }.toString() - val quotedResult = Matcher.quoteReplacement(jsResult) - matcher.appendReplacement(stringBuffer, quotedResult) - } else { - matcher.appendReplacement(stringBuffer, replacement1) + Coroutine.async(executeContext = IO) { + val job = launch { + try { + val pattern = regex.toPattern() + val matcher = pattern.matcher(charSequence) + val stringBuffer = StringBuffer() + while (matcher.find()) { + if (isJs) { + val jsResult = RhinoScriptEngine.run { + val bindings = ScriptBindings() + bindings["result"] = matcher.group() + eval(replacement1, bindings) + }.toString() + val quotedResult = Matcher.quoteReplacement(jsResult) + matcher.appendReplacement(stringBuffer, quotedResult) + } else { + matcher.appendReplacement(stringBuffer, replacement1) + } } + matcher.appendTail(stringBuffer) + block.resume(stringBuffer.toString()) + } catch (e: Exception) { + block.resumeWithException(e) } - matcher.appendTail(stringBuffer) - block.resume(stringBuffer.toString()) - } catch (e: Exception) { - block.resumeWithException(e) } - } - handler.postDelayed(timeout) { - if (coroutine.isActive) { - val timeoutMsg = - "替换超时,3秒后还未结束将重启应用\n替换规则$regex\n替换内容:$charSequence" - val exception = RegexTimeoutException(timeoutMsg) - block.cancel(exception) - appCtx.longToastOnUi(timeoutMsg) - CrashHandler.saveCrashInfo2File(exception) - handler.postDelayed(3000) { - if (coroutine.isActive) { - appCtx.restart() + select { + job.onJoin {} + onTimeout(timeout) { + val timeoutMsg = + "替换超时,3秒后还未结束将重启应用\n替换规则$regex\n替换内容:$charSequence" + val exception = RegexTimeoutException(timeoutMsg) + block.cancel(exception) + appCtx.longToastOnUi(timeoutMsg) + CrashHandler.saveCrashInfo2File(exception) + select { + job.onJoin {} + onTimeout(3000) { + appCtx.restart() + } } } }