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

优化章节缓存
pull/2102/head
kunfei 2 years ago committed by GitHub
commit 37a057b2c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
  2. 30
      app/src/main/java/io/legado/app/model/CacheBook.kt
  3. 7
      app/src/main/java/io/legado/app/service/CacheBookService.kt
  4. 1
      app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt
  5. 4
      app/src/main/java/io/legado/app/ui/book/cache/CacheAdapter.kt
  6. 11
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt

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

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

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

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

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

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

Loading…
Cancel
Save