kunfei 4 лет назад
Родитель
Сommit
f4b4fd659b

+ 0 - 3
app/build.gradle

@@ -212,9 +212,6 @@ dependencies {
     //implementation('com.github.gedoor:rhino-android:1.8')
     implementation(fileTree(dir: 'lib', include: ['rhino-*.jar']))
 
-    //正则匹配,android自带的正则匹配出现超时问题无法处理
-    implementation('com.google.re2j:re2j:1.7')
-
     //网络
     implementation('com.squareup.okhttp3:okhttp:4.10.0')
     implementation(fileTree(dir: 'cronetlib', include: ['*.jar', '*.aar']))

+ 3 - 3
app/src/main/java/io/legado/app/data/entities/BookChapter.kt

@@ -100,10 +100,10 @@ data class BookChapter(
                 if (item.pattern.isNotEmpty()) {
                     try {
                         val mDisplayTitle = if (item.isRegex) {
-                            displayTitle.replaceRegex(
-                                item.pattern,
+                            displayTitle.replace(
+                                item.pattern.toRegex(),
                                 item.replacement
-                            ).getOrThrow()
+                            )
                         } else {
                             displayTitle.replace(item.pattern, item.replacement)
                         }

+ 3 - 4
app/src/main/java/io/legado/app/help/book/ContentProcessor.kt

@@ -9,7 +9,6 @@ import io.legado.app.data.entities.ReplaceRule
 import io.legado.app.exception.RegexTimeoutException
 import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.ReadBookConfig
-import io.legado.app.utils.replaceRegex
 import io.legado.app.utils.stackTraceStr
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.CancellationException
@@ -141,10 +140,10 @@ class ContentProcessor private constructor(
             if (item.pattern.isNotEmpty()) {
                 try {
                     mContent = if (item.isRegex) {
-                        mContent.replaceRegex(
-                            item.pattern,
+                        mContent.replace(
+                            item.pattern.toRegex(),
                             item.replacement
-                        ).getOrThrow()
+                        )
                     } else {
                         mContent.replace(item.pattern, item.replacement)
                     }

+ 52 - 22
app/src/main/java/io/legado/app/utils/RegexExtensions.kt

@@ -1,34 +1,64 @@
 package io.legado.app.utils
 
-import com.google.re2j.Pattern
+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() }
 
 /**
- * 带有js功能的正则替换
- * 采用com.google.re2j.Pattern,不会导致无限执行
+ * 带有超时检测的正则替换
  */
-fun CharSequence.replaceRegex(regex: String, replacement: String): Result<String> {
-    return kotlin.runCatching {
-        val charSequence = this
-        val isJs = replacement.startsWith("@js:")
-        val replacement1 = if (isJs) replacement.substring(4) else replacement
-        val pattern = Pattern.compile(regex)
-        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)
+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
+    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)
+            }
+        }
+        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)
-        stringBuffer.toString()
     }
 }