From 59afc9dd9412dfb7376071afa96cccf02058a2af Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Sat, 16 May 2026 01:14:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/model/CacheBook.kt | 2 +- .../io/legado/app/model/CacheBookModel.kt | 19 ++++++++++-- .../app/model/cache/CacheDownloadQueue.kt | 16 +++++++++- .../io/legado/app/service/CacheBookService.kt | 29 +++++++++---------- 4 files changed, 47 insertions(+), 19 deletions(-) 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 cedc34987..d5d215ea6 100644 --- a/app/src/main/java/io/legado/app/model/CacheBook.kt +++ b/app/src/main/java/io/legado/app/model/CacheBook.kt @@ -78,7 +78,7 @@ object CacheBook { } var emitted = false taskMap.forEach { (_, model) -> - if (model.hasRunnableDownloads()) { + if (model.hasLaunchableChapters()) { emit(model) emitted = true } diff --git a/app/src/main/java/io/legado/app/model/CacheBookModel.kt b/app/src/main/java/io/legado/app/model/CacheBookModel.kt index e0e151d9c..6393628b9 100644 --- a/app/src/main/java/io/legado/app/model/CacheBookModel.kt +++ b/app/src/main/java/io/legado/app/model/CacheBookModel.kt @@ -153,6 +153,16 @@ class CacheBookModel( ) } + /** + * 有可启动的下载任务(队列中有待下载章节或正在加载目录)。 + * 与 [hasRunnableDownloads] 不同,不包含已在执行中的任务。 + * 用于判断是否需要向下载循环 emit 模型,避免无新章节可取时的高频空转。 + */ + @Synchronized + fun hasLaunchableChapters(): Boolean { + return !isPaused && (queue.waitingCount() > 0 || isLoading) + } + @Synchronized fun diagnostics(): Diagnostics { return Diagnostics( @@ -579,7 +589,7 @@ class CacheBookModel( return true } } - repository.downloadContentTask( + val task = repository.downloadContentTask( scope = scope, bookSource = bookSource, book = book, @@ -604,8 +614,13 @@ class CacheBookModel( onCancel(chapter.index, requeue = false) downloadFinish(chapter, "download canceled", resetPageOffset, true) }.onFinally { + chapterTasks.remove(chapter.index) host.onTaskQueuesChanged(book.bookUrl) - }.start() + } + task.start() + synchronized(this) { + chapterTasks[chapter.index] = task + } return true } diff --git a/app/src/main/java/io/legado/app/model/cache/CacheDownloadQueue.kt b/app/src/main/java/io/legado/app/model/cache/CacheDownloadQueue.kt index 773919bdd..12e140631 100644 --- a/app/src/main/java/io/legado/app/model/cache/CacheDownloadQueue.kt +++ b/app/src/main/java/io/legado/app/model/cache/CacheDownloadQueue.kt @@ -172,7 +172,21 @@ class CacheDownloadQueue { val indexCount = indices.count { !emittedIndices.contains(it) && !removedIndices.contains(it) } - val rangeCount = ranges.sumOf { it.remainingCount(emittedIndices, removedIndices) } + val rangeCount = if (ranges.size <= 1) { + ranges.sumOf { it.remainingCount(emittedIndices, removedIndices) } + } else { + val seen = mutableSetOf() + ranges.forEach { cursor -> + var i = cursor.next + while (i <= cursor.end) { + if (!emittedIndices.contains(i) && !removedIndices.contains(i)) { + seen.add(i) + } + i++ + } + } + seen.size + } return indexCount + rangeCount } diff --git a/app/src/main/java/io/legado/app/service/CacheBookService.kt b/app/src/main/java/io/legado/app/service/CacheBookService.kt index 13016c298..a0a7b85e3 100644 --- a/app/src/main/java/io/legado/app/service/CacheBookService.kt +++ b/app/src/main/java/io/legado/app/service/CacheBookService.kt @@ -12,6 +12,7 @@ import io.legado.app.constant.NotificationId import io.legado.app.data.appDb import io.legado.app.help.book.update import io.legado.app.model.CacheBook +import io.legado.app.model.CacheBookModel import io.legado.app.model.cache.CacheDownloadAdmissionQueue import io.legado.app.model.cache.CacheDownloadRequest import io.legado.app.model.cache.CacheDownloadSource @@ -24,11 +25,11 @@ import io.legado.app.utils.activityPendingIntent import io.legado.app.utils.servicePendingIntent import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay @@ -310,10 +311,10 @@ class CacheBookService : BaseService() { kotlin.runCatching { WebBook.getBookInfoAwait(cacheBook.bookSource, book) }.onFailure { - removeDownload(request.bookUrl) markBookAdmissionFailed( request.bookUrl, - getString(R.string.error_get_book_info) + getString(R.string.error_get_book_info), + model = cacheBook, ) AppLog.put( "《$name》目录为空且加载详情页失败\n${it.localizedMessage}", @@ -333,10 +334,10 @@ class CacheBookService : BaseService() { book.totalChapterNum = 0 book.update() } - removeDownload(request.bookUrl) markBookAdmissionFailed( request.bookUrl, - getString(R.string.error_get_chapter_list) + getString(R.string.error_get_chapter_list), + model = cacheBook, ) AppLog.put( "《$name》目录为空且加载目录失败\n${it.localizedMessage}", @@ -408,8 +409,15 @@ class CacheBookService : BaseService() { return removedQueued || removedAdmission || removedActive } - private fun markBookAdmissionFailed(bookUrl: String, message: String) { + private fun markBookAdmissionFailed( + bookUrl: String, + message: String, + model: CacheBookModel? = null, + ) { removeQueuedBook(bookUrl) + if (model != null) { + CacheBook.removeModelFromService(bookUrl, model) + } CacheBook.markBookFailed(bookUrl, message) } @@ -476,15 +484,6 @@ class CacheBookService : BaseService() { } - private fun removeDownload(bookUrl: String?) { - CacheBook.cacheBookMap[bookUrl]?.stop() - if (CacheBook.isRun) { - ensureDownloadJob() - return - } - stopIfIdle() - } - private suspend fun runDownloadLoop() { try { var idleSince = -1L