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 4610ae334..6bb8b846e 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 @@ -19,6 +19,7 @@ import kotlin.coroutines.CoroutineContext /** * 链式协程 + * 注意:如果协程太快完成,回调会不执行 */ @Suppress("unused", "MemberVisibilityCanBePrivate") class Coroutine( diff --git a/app/src/main/java/io/legado/app/model/CacheBook.kt b/app/src/main/java/io/legado/app/model/CacheBook.kt index 7bba3acc0..a22778624 100644 --- a/app/src/main/java/io/legado/app/model/CacheBook.kt +++ b/app/src/main/java/io/legado/app/model/CacheBook.kt @@ -18,6 +18,7 @@ import io.legado.app.utils.postEvent import io.legado.app.utils.startService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.delay @@ -268,6 +269,7 @@ object CacheBook { book, chapter, context = context, + start = CoroutineStart.LAZY, executeContext = context ).onSuccess { content -> onSuccess(chapter) @@ -282,7 +284,7 @@ object CacheBook { onCancel(chapterIndex) }.onFinally { onFinally() - } + }.start() } @Synchronized @@ -297,22 +299,28 @@ object CacheBook { postEvent(EventBus.UP_DOWNLOAD, book.bookUrl) onDownloadSet.add(chapter.index) waitDownloadSet.remove(chapter.index) - WebBook.getContent(scope, bookSource, book, chapter, executeContext = IO) - .onSuccess { content -> - onSuccess(chapter) - ReadBook.downloadedChapters.add(chapter.index) - ReadBook.downloadFailChapters.remove(chapter.index) - downloadFinish(chapter, content, resetPageOffset) - }.onError { - onError(chapter, it) - ReadBook.downloadFailChapters[chapter.index] = - (ReadBook.downloadFailChapters[chapter.index] ?: 0) + 1 - downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}", resetPageOffset) - }.onCancel { - onCancel(chapter.index) - }.onFinally { - postEvent(EventBus.UP_DOWNLOAD, book.bookUrl) - } + WebBook.getContent( + scope, + bookSource, + book, + chapter, + start = CoroutineStart.LAZY, + executeContext = IO + ).onSuccess { content -> + onSuccess(chapter) + ReadBook.downloadedChapters.add(chapter.index) + ReadBook.downloadFailChapters.remove(chapter.index) + downloadFinish(chapter, content, resetPageOffset) + }.onError { + onError(chapter, it) + ReadBook.downloadFailChapters[chapter.index] = + (ReadBook.downloadFailChapters[chapter.index] ?: 0) + 1 + downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}", resetPageOffset) + }.onCancel { + onCancel(chapter.index) + }.onFinally { + postEvent(EventBus.UP_DOWNLOAD, book.bookUrl) + }.start() } private fun downloadFinish( diff --git a/app/src/main/java/io/legado/app/model/webBook/SearchModel.kt b/app/src/main/java/io/legado/app/model/webBook/SearchModel.kt index 1ac0e0c06..2cc665390 100644 --- a/app/src/main/java/io/legado/app/model/webBook/SearchModel.kt +++ b/app/src/main/java/io/legado/app/model/webBook/SearchModel.kt @@ -11,6 +11,7 @@ import io.legado.app.help.coroutine.CompositeCoroutine import io.legado.app.ui.book.search.SearchScope import io.legado.app.utils.getPrefBoolean import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.ensureActive @@ -76,7 +77,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB return } searchIndex++ - val source = bookSourceList[searchIndex] + val source = bookSourceList.getOrNull(searchIndex) ?: return val searchPool = searchPool ?: return val task = WebBook.searchBook( scope, @@ -84,6 +85,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB searchKey, searchPage, context = searchPool, + start = CoroutineStart.LAZY, executeContext = searchPool ).timeout(30000L) .onSuccess { @@ -93,6 +95,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB .onFinally { onFinally(searchId) } + task.start() tasks.add(task) } diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 0592a50ef..53cb00113 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -14,6 +14,7 @@ import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.RuleData import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive import kotlin.coroutines.CoroutineContext @@ -30,9 +31,10 @@ object WebBook { key: String, page: Int? = 1, context: CoroutineContext = Dispatchers.IO, + start: CoroutineStart = CoroutineStart.DEFAULT, executeContext: CoroutineContext = Dispatchers.Main, ): Coroutine> { - return Coroutine.async(scope, context, executeContext = executeContext) { + return Coroutine.async(scope, context, start = start, executeContext = executeContext) { searchBookAwait(bookSource, key, page) } } @@ -266,9 +268,10 @@ object WebBook { nextChapterUrl: String? = null, needSave: Boolean = true, context: CoroutineContext = Dispatchers.IO, + start: CoroutineStart = CoroutineStart.DEFAULT, executeContext: CoroutineContext = Dispatchers.Main, ): Coroutine { - return Coroutine.async(scope, context, executeContext = executeContext) { + return Coroutine.async(scope, context, start = start, executeContext = executeContext) { getContentAwait(bookSource, book, bookChapter, nextChapterUrl, needSave) } } diff --git a/app/src/main/java/io/legado/app/service/AudioPlayService.kt b/app/src/main/java/io/legado/app/service/AudioPlayService.kt index 9b8aa7df0..e90a52e3d 100644 --- a/app/src/main/java/io/legado/app/service/AudioPlayService.kt +++ b/app/src/main/java/io/legado/app/service/AudioPlayService.kt @@ -452,7 +452,7 @@ class AudioPlayService : BaseService(), .setBufferedPosition(exoPlayer.bufferedPosition) .addCustomAction( APP_ACTION_STOP, - getString(R.string.set_timer), + getString(R.string.stop), R.drawable.ic_stop_black_24dp ) .addCustomAction( diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt index b35eadb82..0ade572c8 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt @@ -200,9 +200,13 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a } ++searchIndex } - val source = bookSourceList[searchIndex] + val source = bookSourceList.getOrNull(searchIndex) ?: return bookSourceList[searchIndex] = emptyBookSource - val task = execute(context = searchPool!!, executeContext = searchPool!!) { + val task = execute( + context = searchPool!!, + start = CoroutineStart.LAZY, + executeContext = searchPool!! + ) { val resultBooks = WebBook.searchBookAwait(source, name) resultBooks.forEach { searchBook -> if (searchBook.name != name) { @@ -223,11 +227,14 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a } }.timeout(60000L) .onError { + ensureActive() nextSearch() } .onSuccess { + ensureActive() nextSearch() } + task.start() tasks.add(task) } @@ -293,8 +300,9 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a searchCallback?.searchSuccess(searchBook) } + @Synchronized private fun nextSearch() { - synchronized(this) { + kotlin.runCatching { if (searchIndex < bookSourceList.lastIndex) { search() } else { diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 92abc3240..babbdf822 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -36,6 +36,7 @@ import org.apache.commons.text.StringEscapeUtils import org.jsoup.Jsoup import java.io.ByteArrayInputStream import java.net.URLDecoder +import java.util.regex.PatternSyntaxException /** * rss阅读界面 @@ -346,24 +347,32 @@ class ReadRssActivity : VMBaseActivity request: WebResourceRequest ): WebResourceResponse? { val url = request.url.toString() - viewModel.rssSource?.let { source -> - val blacklist = source.contentBlacklist?.splitNotBlank(",") - if (!blacklist.isNullOrEmpty()) { - blacklist.forEach { + val source = viewModel.rssSource ?: return super.shouldInterceptRequest(view, request) + val blacklist = source.contentBlacklist?.splitNotBlank(",") + if (!blacklist.isNullOrEmpty()) { + blacklist.forEach { + try { if (url.startsWith(it) || url.matches(it.toRegex())) { return createEmptyResource() } + } catch (e: PatternSyntaxException) { + AppLog.put("黑名单规则正则语法错误 源名称:${source.sourceName} 正则:$it", e) } - } else { - val whitelist = source.contentWhitelist?.splitNotBlank(",") - if (!whitelist.isNullOrEmpty()) { - whitelist.forEach { + } + } else { + val whitelist = source.contentWhitelist?.splitNotBlank(",") + if (!whitelist.isNullOrEmpty()) { + whitelist.forEach { + try { if (url.startsWith(it) || url.matches(it.toRegex())) { return super.shouldInterceptRequest(view, request) } + } catch (e: PatternSyntaxException) { + val msg = "白名单规则正则语法错误 源名称:${source.sourceName} 正则:$it" + AppLog.put(msg, e) } - return createEmptyResource() } + return createEmptyResource() } } return super.shouldInterceptRequest(view, request) diff --git a/app/src/main/java/io/legado/app/utils/ToastUtils.kt b/app/src/main/java/io/legado/app/utils/ToastUtils.kt index 91440ce14..da96ee53a 100644 --- a/app/src/main/java/io/legado/app/utils/ToastUtils.kt +++ b/app/src/main/java/io/legado/app/utils/ToastUtils.kt @@ -4,6 +4,7 @@ package io.legado.app.utils import android.annotation.SuppressLint import android.content.Context +import android.view.View import android.widget.TextView import android.widget.Toast import androidx.cardview.widget.CardView @@ -28,12 +29,10 @@ fun Context.toastOnUi(message: Int, duration: Int = Toast.LENGTH_SHORT) { fun Context.toastOnUi(message: CharSequence?, duration: Int = Toast.LENGTH_SHORT) { runOnUI { kotlin.runCatching { - if (toast == null || BuildConfig.DEBUG || AppConfig.recordLog) { - toast?.cancel() - toast = Toast(this) - toast?.view = inflate(R.layout.view_toast) - } - val toastView = toast?.view!! + toast?.cancel() + toast = Toast(this) + val toastView: View = inflate(R.layout.view_toast) + toast?.view = toastView val cardView = toastView.findViewById(R.id.cv_content) cardView.setCardBackgroundColor(bottomBackground) val isLight = ColorUtils.isColorLight(bottomBackground)