Kaynağa Gözat

Merge pull request #2099 from 821938089/opt-cache-manager

优化章节缓存
kunfei 4 yıl önce
ebeveyn
işleme
44aa5d9518

+ 4 - 0
app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt

@@ -40,6 +40,8 @@ class Coroutine<T>(
     private var timeMillis: Long? = null
     private var errorReturn: Result<T>? = null
 
+    private var isCancelCalled = false
+
     val isCancelled: Boolean
         get() = job.isCancelled
 
@@ -115,6 +117,7 @@ class Coroutine<T>(
 
     //取消当前任务
     fun cancel(cause: CancellationException? = null) {
+        isCancelCalled = true
         job.cancel(cause)
         cancel?.let {
             MainScope().launch {
@@ -146,6 +149,7 @@ class Coroutine<T>(
                 success?.let { dispatchCallback(this, value, it) }
             } catch (e: Throwable) {
                 e.printOnDebug()
+                if (e is CancellationException && !isCancelCalled) this@Coroutine.cancel()
                 if (e is CancellationException && e !is TimeoutCancellationException) {
                     return@launch
                 }

+ 26 - 4
app/src/main/java/io/legado/app/model/CacheBook.kt

@@ -135,6 +135,8 @@ object CacheBook {
         private val onDownloadSet = linkedSetOf<Int>()
         private val successDownloadSet = linkedSetOf<Int>()
         private val errorDownloadMap = hashMapOf<Int, Int>()
+        private var isStopped = false
+        private var waitingRetry = false
 
         val waitCount get() = waitDownloadSet.size
         val onDownloadCount get() = onDownloadSet.size
@@ -146,13 +148,20 @@ object CacheBook {
             return waitDownloadSet.size > 0 || onDownloadSet.size > 0
         }
 
+        @Synchronized
+        fun isStop(): Boolean {
+            return isStopped || (!isRun() && !waitingRetry)
+        }
+
         @Synchronized
         fun stop() {
             waitDownloadSet.clear()
+            isStopped = true
         }
 
         @Synchronized
         fun addDownload(start: Int, end: Int) {
+            isStopped = false
             for (i in start..end) {
                 if (!onDownloadSet.contains(i)) {
                     waitDownloadSet.add(i)
@@ -168,13 +177,18 @@ object CacheBook {
         }
 
         @Synchronized
-        private fun onError(index: Int, error: Throwable, chapterTitle: String) {
+        private fun onPreError(index: Int, error: Throwable) {
+            waitingRetry = true
             if (error !is ConcurrentException) {
                 errorDownloadMap[index] = (errorDownloadMap[index] ?: 0) + 1
             }
             onDownloadSet.remove(index)
+        }
+
+        @Synchronized
+        private fun onPostError(index: Int, error: Throwable, chapterTitle: String) {
             //重试3次
-            if ((errorDownloadMap[index] ?: 0) < 3) {
+            if ((errorDownloadMap[index] ?: 0) < 3 && !isStopped) {
                 waitDownloadSet.add(index)
             } else {
                 AppLog.put(
@@ -182,12 +196,19 @@ object CacheBook {
                     error
                 )
             }
+            waitingRetry = false
+        }
+
+        @Synchronized
+        private fun onError(index: Int, error: Throwable, chapterTitle: String) {
+            onPreError(index, error)
+            onPostError(index, error, chapterTitle)
         }
 
         @Synchronized
         private fun onCancel(index: Int) {
             onDownloadSet.remove(index)
-            waitDownloadSet.add(index)
+            if (!isStopped) waitDownloadSet.add(index)
         }
 
         @Synchronized
@@ -234,9 +255,10 @@ object CacheBook {
                 onSuccess(chapterIndex)
                 downloadFinish(chapter, content)
             }.onError {
+                onPreError(chapterIndex, it)
                 //出现错误等待一秒后重新加入待下载列表
                 delay(1000)
-                onError(chapterIndex, it, chapter.title)
+                onPostError(chapterIndex, it, chapter.title)
                 downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}")
             }.onCancel {
                 onCancel(chapterIndex)

+ 7 - 0
app/src/main/java/io/legado/app/service/CacheBookService.kt

@@ -74,6 +74,7 @@ class CacheBookService : BaseService() {
     override fun onDestroy() {
         isRun = false
         cachePool.close()
+        CacheBook.cacheBookMap.forEach { it.value.stop() }
         CacheBook.cacheBookMap.clear()
         super.onDestroy()
         postEvent(EventBus.UP_DOWNLOAD, "")
@@ -84,6 +85,7 @@ class CacheBookService : BaseService() {
         execute {
             val cacheBook = CacheBook.getOrCreate(bookUrl) ?: return@execute
             cacheBook.addDownload(start, end)
+            postEvent(EventBus.UP_DOWNLOAD, "")
             upNotification(CacheBook.downloadSummary)
             if (downloadJob == null) {
                 download()
@@ -93,6 +95,11 @@ class CacheBookService : BaseService() {
 
     private fun removeDownload(bookUrl: String?) {
         CacheBook.cacheBookMap[bookUrl]?.stop()
+        postEvent(EventBus.UP_DOWNLOAD, "")
+        if (downloadJob == null && CacheBook.isRun) {
+            download()
+            return
+        }
         if (CacheBook.cacheBookMap.isEmpty()) {
             stopSelf()
         }

+ 1 - 0
app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt

@@ -237,6 +237,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
         observeEvent<BookChapter>(EventBus.SAVE_CONTENT) {
             adapter.cacheChapters[it.bookUrl]?.add(it.url)
         }
+        postEventDelay(EventBus.UP_DOWNLOAD, "", 100)
     }
 
     override fun export(position: Int) {

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/cache/CacheAdapter.kt

@@ -67,7 +67,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
             ivDownload.setOnClickListener {
                 getItem(holder.layoutPosition)?.let { book ->
                     CacheBook.cacheBookMap[book.bookUrl]?.let {
-                        if (it.isRun()) {
+                        if (!it.isStop()) {
                             CacheBook.remove(context, book.bookUrl)
                         } else {
                             CacheBook.start(context, book, 0, book.totalChapterNum)
@@ -89,7 +89,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
         } else {
             iv.visible()
             CacheBook.cacheBookMap[book.bookUrl]?.let {
-                if (it.isRun()) {
+                if (!it.isStop()) {
                     iv.setImageResource(R.drawable.ic_stop_black_24dp)
                 } else {
                     iv.setImageResource(R.drawable.ic_play_24dp)

+ 7 - 6
app/src/main/java/io/legado/app/ui/main/MainViewModel.kt

@@ -186,13 +186,14 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
     private fun cacheBook() {
         cacheBookJob?.cancel()
         cacheBookJob = viewModelScope.launch(upTocPool) {
-            while (isActive) {
-                if (CacheBookService.isRun) {
-                    cacheBookJob?.cancel()
-                    cacheBookJob = null
-                    return@launch
+            launch {
+                while (isActive) {
+                    postEvent(EventBus.UP_DOWNLOAD, "")
+                    delay(1000)
                 }
-                if (!CacheBook.isRun) {
+            }
+            while (isActive) {
+                if (CacheBookService.isRun || !CacheBook.isRun) {
                     cacheBookJob?.cancel()
                     cacheBookJob = null
                     return@launch