HapeLee 5 месяцев назад
Родитель
Сommit
59afc9dd94

+ 1 - 1
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
                         }

+ 17 - 2
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
     }
 

+ 15 - 1
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<Int>()
+            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
     }
 

+ 14 - 15
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